initial wasm32 'support' (#443)
* Give tungstenite types distinct names * reorganize files * Better feature locking, add wasm.rs * Implement wasm Backend * add wasm-bindgen-test * Build & Test for wasm * Add macos safari wasm test * Add wasm32 target * Add wasm.rs test * Move wasm-pack installation before test execution * Fix build on wasm32 * Fix examples depending on tokio::time * fix clippy warn * Add example wasm bindgen test * Add wasm-bindgen to Cargo.toml * Add wasm test configuration * Install wasm-bindgen-cli on linux * Add wasm-bindgen-cli to macos * Correct "vers" to "version" * Attempt to locate correct geckodriver * Run wasm tests first * maybe this will fix ci :clueless: * Move wasm-bindgen-cli install * Add cargo-binstall installation script for wasm-bindgen-cli * Try using only one browser * remove geckodriver * Move all wasm related tests to macos * Rename macOS test step for clarity * Try out combined coverage report * try different strategy to skip coverage on forks * Revert "try different strategy to skip coverage on forks" This reverts commitcae64ae5a4
. * Revert "Try out combined coverage report" This reverts commit584fd21076
.
This commit is contained in:
parent
c7fdb04e6b
commit
79353731b6
|
@ -0,0 +1,2 @@
|
|||
[target.wasm32-unknown-unknown]
|
||||
runner = 'wasm-bindgen-test-runner'
|
|
@ -10,7 +10,7 @@ env:
|
|||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
rust:
|
||||
linux:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
@ -33,6 +33,7 @@ jobs:
|
|||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-all-crates: "true"
|
||||
prefix-key: "linux"
|
||||
- name: Build, Test and Publish Coverage
|
||||
run: |
|
||||
if [ -n "${{ secrets.COVERALLS_REPO_TOKEN }}" ]; then
|
||||
|
@ -44,4 +45,33 @@ jobs:
|
|||
cargo build --verbose --all-features
|
||||
cargo test --verbose --all-features
|
||||
fi
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Clone spacebar server
|
||||
run: |
|
||||
git clone https://github.com/bitfl0wer/server.git
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
cache-dependency-path: server/package-lock.json
|
||||
- name: Prepare and start Spacebar server
|
||||
run: |
|
||||
npm install
|
||||
npm run setup
|
||||
npm run start &
|
||||
working-directory: ./server
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-all-crates: "true"
|
||||
prefix-key: "macos"
|
||||
- name: Run WASM tests with Safari, Firefox, Chrome
|
||||
run: |
|
||||
rustup target add wasm32-unknown-unknown
|
||||
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
||||
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force
|
||||
SAFARIDRIVER=$(which safaridriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt"
|
||||
GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt"
|
||||
CHROMEDRIVER=$(which chromedriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt"
|
|
@ -205,7 +205,6 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"log",
|
||||
"native-tls",
|
||||
"pharos",
|
||||
"poem",
|
||||
"rand",
|
||||
"regex",
|
||||
|
@ -223,6 +222,8 @@ dependencies = [
|
|||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
"ws_stream_wasm",
|
||||
]
|
||||
|
||||
|
@ -252,6 +253,16 @@ dependencies = [
|
|||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.5"
|
||||
|
@ -1674,6 +1685,12 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
|
@ -2627,6 +2644,31 @@ version = "0.2.88"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6433b7c56db97397842c46b67e11873eda263170afeb3a2dc74a7cb370fee0d"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
"scoped-tls",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-bindgen-test-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "493fcbab756bb764fa37e6bee8cec2dd709eb4273d06d0c282a5e74275ded735"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.65"
|
||||
|
|
|
@ -54,9 +54,6 @@ sqlx = { version = "0.7.1", features = [
|
|||
], optional = true }
|
||||
safina-timer = "0.1.11"
|
||||
rand = "0.8.5"
|
||||
# TODO: Remove the below 2 imports for production!
|
||||
ws_stream_wasm = "0.7.4"
|
||||
pharos = "0.5.3"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
rustls = "0.21.8"
|
||||
|
@ -70,10 +67,10 @@ hostname = "0.3.1"
|
|||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
getrandom = { version = "0.2.11", features = ["js"] }
|
||||
tokio-tungstenite = { version = "0.20.1", default-features = false }
|
||||
ws_stream_wasm = "0.7.4"
|
||||
pharos = "0.5.3"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
wasm-bindgen-test = "0.3.38"
|
||||
wasm-bindgen = "0.2.88"
|
||||
|
|
|
@ -6,7 +6,7 @@ use chorus::{
|
|||
types::{GatewayIdentifyPayload, GatewayReady},
|
||||
};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use tokio::{self, time::sleep};
|
||||
use tokio::{self};
|
||||
|
||||
// This example creates a simple gateway connection and a basic observer struct
|
||||
|
||||
|
@ -54,9 +54,10 @@ async fn main() {
|
|||
let mut identify = GatewayIdentifyPayload::common();
|
||||
identify.token = token;
|
||||
gateway.send_identify(identify).await;
|
||||
safina_timer::start_timer_thread();
|
||||
|
||||
// Do something on the main thread so we don't quit
|
||||
loop {
|
||||
sleep(Duration::MAX).await;
|
||||
safina_timer::sleep_for(Duration::MAX).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use std::time::Duration;
|
|||
|
||||
use chorus::gateway::Gateway;
|
||||
use chorus::{self, types::GatewayIdentifyPayload};
|
||||
use tokio::time::sleep;
|
||||
|
||||
/// This example creates a simple gateway connection and a session with an Identify event
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
|
@ -11,7 +10,7 @@ async fn main() {
|
|||
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
|
||||
|
||||
// Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another
|
||||
let gateway = Gateway::spawn(websocket_url_spacebar).await.unwrap();
|
||||
let _ = Gateway::spawn(websocket_url_spacebar).await.unwrap();
|
||||
|
||||
// At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated
|
||||
|
||||
|
@ -27,10 +26,10 @@ async fn main() {
|
|||
identify.token = token;
|
||||
|
||||
// Send off the event
|
||||
gateway.send_identify(identify).await;
|
||||
safina_timer::start_timer_thread();
|
||||
|
||||
// Do something on the main thread so we don't quit
|
||||
loop {
|
||||
sleep(Duration::MAX).await;
|
||||
safina_timer::sleep_for(Duration::MAX).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))]
|
||||
pub mod tungstenite;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))]
|
||||
pub use tungstenite::*;
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "client"))]
|
||||
pub mod wasm;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "client"))]
|
||||
pub use wasm::*;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))]
|
||||
pub type Sink = tungstenite::TungsteniteSink;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))]
|
||||
pub type Stream = tungstenite::TungsteniteStream;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))]
|
||||
pub type WebSocketBackend = tungstenite::TungsteniteBackend;
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "client"))]
|
||||
pub type Sink = wasm::WasmSink;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "client"))]
|
||||
pub type Stream = wasm::WasmStream;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "client"))]
|
||||
pub type WebSocketBackend = wasm::WasmBackend;
|
|
@ -7,20 +7,21 @@ use tokio_tungstenite::{
|
|||
connect_async_tls_with_config, tungstenite, Connector, MaybeTlsStream, WebSocketStream,
|
||||
};
|
||||
|
||||
use super::GatewayMessage;
|
||||
use crate::errors::GatewayError;
|
||||
use crate::gateway::GatewayMessage;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WebSocketBackend;
|
||||
pub struct TungsteniteBackend;
|
||||
|
||||
// These could be made into inherent associated types when that's stabilized
|
||||
pub type WsSink = SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, tungstenite::Message>;
|
||||
pub type WsStream = SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>;
|
||||
pub type TungsteniteSink =
|
||||
SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, tungstenite::Message>;
|
||||
pub type TungsteniteStream = SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>;
|
||||
|
||||
impl WebSocketBackend {
|
||||
impl TungsteniteBackend {
|
||||
pub async fn connect(
|
||||
websocket_url: &str,
|
||||
) -> Result<(WsSink, WsStream), crate::errors::GatewayError> {
|
||||
) -> Result<(TungsteniteSink, TungsteniteStream), crate::errors::GatewayError> {
|
||||
let mut roots = rustls::RootCertStore::empty();
|
||||
for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs")
|
||||
{
|
|
@ -0,0 +1,50 @@
|
|||
use futures_util::{
|
||||
stream::{SplitSink, SplitStream},
|
||||
StreamExt,
|
||||
};
|
||||
|
||||
use ws_stream_wasm::*;
|
||||
|
||||
use crate::errors::GatewayError;
|
||||
use crate::gateway::GatewayMessage;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WasmBackend;
|
||||
|
||||
// These could be made into inherent associated types when that's stabilized
|
||||
pub type WasmSink = SplitSink<WsStream, WsMessage>;
|
||||
pub type WasmStream = SplitStream<WsStream>;
|
||||
|
||||
impl WasmBackend {
|
||||
pub async fn connect(
|
||||
websocket_url: &str,
|
||||
) -> Result<(WasmSink, WasmStream), crate::errors::GatewayError> {
|
||||
let (_, websocket_stream) = match WsMeta::connect(websocket_url, None).await {
|
||||
Ok(stream) => Ok(stream),
|
||||
Err(e) => Err(GatewayError::CannotConnect {
|
||||
error: e.to_string(),
|
||||
}),
|
||||
}?;
|
||||
|
||||
Ok(websocket_stream.split())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GatewayMessage> for WsMessage {
|
||||
fn from(message: GatewayMessage) -> Self {
|
||||
Self::Text(message.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WsMessage> for GatewayMessage {
|
||||
fn from(value: WsMessage) -> Self {
|
||||
match value {
|
||||
WsMessage::Text(text) => Self(text),
|
||||
WsMessage::Binary(bin) => {
|
||||
let mut text = String::new();
|
||||
let _ = bin.iter().map(|v| text.push_str(&v.to_string()));
|
||||
Self(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ use tokio::task;
|
|||
|
||||
use self::event::Events;
|
||||
use super::*;
|
||||
use super::{WsSink, WsStream};
|
||||
use super::{Sink, Stream};
|
||||
use crate::types::{
|
||||
self, AutoModerationRule, AutoModerationRuleUpdate, Channel, ChannelCreate, ChannelDelete,
|
||||
ChannelUpdate, Guild, GuildRoleCreate, GuildRoleUpdate, JsonField, RoleObject, SourceUrlField,
|
||||
|
@ -17,8 +17,8 @@ use crate::types::{
|
|||
pub struct Gateway {
|
||||
events: Arc<Mutex<Events>>,
|
||||
heartbeat_handler: HeartbeatHandler,
|
||||
websocket_send: Arc<Mutex<WsSink>>,
|
||||
websocket_receive: WsStream,
|
||||
websocket_send: Arc<Mutex<Sink>>,
|
||||
websocket_receive: Stream,
|
||||
kill_send: tokio::sync::broadcast::Sender<()>,
|
||||
store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
|
||||
url: String,
|
||||
|
@ -37,7 +37,10 @@ impl Gateway {
|
|||
|
||||
// Wait for the first hello and then spawn both tasks so we avoid nested tasks
|
||||
// This automatically spawns the heartbeat task, but from the main thread
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let msg: GatewayMessage = websocket_receive.next().await.unwrap().unwrap().into();
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let msg: GatewayMessage = websocket_receive.next().await.unwrap().into();
|
||||
let gateway_payload: types::GatewayReceivePayload = serde_json::from_str(&msg.0).unwrap();
|
||||
|
||||
if gateway_payload.op_code != GATEWAY_HELLO {
|
||||
|
@ -91,11 +94,18 @@ impl Gateway {
|
|||
loop {
|
||||
let msg = self.websocket_receive.next().await;
|
||||
|
||||
// PRETTYFYME: Remove inline conditional compiling
|
||||
// This if chain can be much better but if let is unstable on stable rust
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if let Some(Ok(message)) = msg {
|
||||
self.handle_message(message.into()).await;
|
||||
continue;
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if let Some(message) = msg {
|
||||
self.handle_message(message.into()).await;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We couldn't receive the next message or it was an error, something is wrong with the websocket, close
|
||||
warn!("GW: Websocket is broken, stopping gateway");
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::types::{self, Composite};
|
|||
pub struct GatewayHandle {
|
||||
pub url: String,
|
||||
pub events: Arc<Mutex<Events>>,
|
||||
pub websocket_send: Arc<Mutex<WsSink>>,
|
||||
pub websocket_send: Arc<Mutex<Sink>>,
|
||||
/// Tells gateway tasks to close
|
||||
pub(super) kill_send: tokio::sync::broadcast::Sender<()>,
|
||||
pub(crate) store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
|
||||
|
|
|
@ -27,7 +27,7 @@ pub(super) struct HeartbeatHandler {
|
|||
impl HeartbeatHandler {
|
||||
pub fn new(
|
||||
heartbeat_interval: Duration,
|
||||
websocket_tx: Arc<Mutex<WsSink>>,
|
||||
websocket_tx: Arc<Mutex<Sink>>,
|
||||
kill_rc: tokio::sync::broadcast::Receiver<()>,
|
||||
) -> Self {
|
||||
let (send, receive) = tokio::sync::mpsc::channel(32);
|
||||
|
@ -49,7 +49,7 @@ impl HeartbeatHandler {
|
|||
/// Can be killed by the kill broadcast;
|
||||
/// If the websocket is closed, will die out next time it tries to send a heartbeat;
|
||||
pub async fn heartbeat_task(
|
||||
websocket_tx: Arc<Mutex<WsSink>>,
|
||||
websocket_tx: Arc<Mutex<Sink>>,
|
||||
heartbeat_interval: Duration,
|
||||
mut receive: Receiver<HeartbeatThreadCommunication>,
|
||||
mut kill_receive: tokio::sync::broadcast::Receiver<()>,
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod backend_tungstenite;
|
||||
pub mod backends;
|
||||
pub mod events;
|
||||
pub mod gateway;
|
||||
pub mod handle;
|
||||
pub mod heartbeat;
|
||||
pub mod message;
|
||||
|
||||
pub use backends::*;
|
||||
pub use gateway::*;
|
||||
pub use handle::*;
|
||||
use heartbeat::*;
|
||||
|
@ -22,13 +22,6 @@ use std::sync::{Arc, RwLock};
|
|||
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub type WsSink = backend_tungstenite::WsSink;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub type WsStream = backend_tungstenite::WsStream;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub type WebSocketBackend = backend_tungstenite::WebSocketBackend;
|
||||
|
||||
// Gateway opcodes
|
||||
/// Opcode received when the server dispatches a [crate::types::WebSocketEvent]
|
||||
const GATEWAY_DISPATCH: u8 = 0;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
#[wasm_bindgen_test]
|
||||
fn pass() {
|
||||
let _ = String::new();
|
||||
}
|
Loading…
Reference in New Issue