1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
// Copyright (c) 2019-2022 Naja Melan
// Copyright (c) 2023-2024 Yuki Kishimoto
// Distributed under the MIT software license
use js_sys::{ArrayBuffer, Uint8Array};
use wasm_bindgen::JsCast;
use web_sys::{Blob, MessageEvent};
use crate::WsErr;
/// Represents a WebSocket Message, after converting from JavaScript type.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum WsMessage {
/// The data of the message is a string.
Text(String),
/// The message contains binary data.
Binary(Vec<u8>),
}
impl WsMessage {
/// Get the length of the WebSocket message.
#[inline]
pub fn len(&self) -> usize {
match self {
Self::Text(string) => string.len(),
Self::Binary(data) => data.len(),
}
}
/// Returns true if the WebSocket message has no content.
/// For example, if the other side of the connection sent an empty string.
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Consume the message and return it as binary data.
pub fn into_data(self) -> Vec<u8> {
match self {
Self::Text(string) => string.into_bytes(),
Self::Binary(data) => data,
}
}
}
/// This will convert the JavaScript event into a WsMessage. Note that this
/// will only work if the connection is set to use the binary type ArrayBuffer.
/// On binary type Blob, this will panic.
impl TryFrom<MessageEvent> for WsMessage {
type Error = WsErr;
fn try_from(evt: MessageEvent) -> Result<Self, Self::Error> {
match evt.data() {
d if d.is_instance_of::<ArrayBuffer>() => {
let buffy = Uint8Array::new(d.unchecked_ref());
let mut v = vec![0; buffy.length() as usize];
buffy.copy_to(&mut v); // FIXME: get rid of this copy
Ok(WsMessage::Binary(v))
}
// We don't allow invalid encodings. In principle if needed,
// we could add a variant to WsMessage with a CString or an OsString
// to allow the user to access this data. However until there is a usecase,
// I'm not inclined, amongst other things because the conversion from Js isn't very
// clear and it would require a bunch of testing for something that's a rather bad
// idea to begin with. If you need data that is not a valid string, use a binary
// message.
d if d.is_string() => match d.as_string() {
Some(text) => Ok(WsMessage::Text(text)),
None => Err(WsErr::InvalidEncoding),
},
// We have set the binary mode to array buffer (WsMeta::connect), so normally this shouldn't happen.
// That is as long as this is used within the context of the WsMeta constructor.
d if d.is_instance_of::<Blob>() => Err(WsErr::CantDecodeBlob),
// should never happen.
_ => Err(WsErr::UnknownDataType),
}
}
}
impl From<WsMessage> for Vec<u8> {
fn from(msg: WsMessage) -> Self {
match msg {
WsMessage::Text(string) => string.into(),
WsMessage::Binary(vec) => vec,
}
}
}
impl From<Vec<u8>> for WsMessage {
fn from(vec: Vec<u8>) -> Self {
WsMessage::Binary(vec)
}
}
impl From<String> for WsMessage {
fn from(s: String) -> Self {
WsMessage::Text(s)
}
}
impl AsRef<[u8]> for WsMessage {
fn as_ref(&self) -> &[u8] {
match self {
WsMessage::Text(string) => string.as_ref(),
WsMessage::Binary(vec) => vec.as_ref(),
}
}
}