2023-04-28 20:29:40 +02:00
|
|
|
use std::sync::Arc;
|
|
|
|
use std::sync::Mutex;
|
|
|
|
use std::thread::JoinHandle;
|
|
|
|
|
2023-04-28 18:18:32 +02:00
|
|
|
use crate::api::types::*;
|
|
|
|
use crate::api::WebSocketEvent;
|
|
|
|
use crate::errors::ObserverError;
|
|
|
|
use crate::gateway::events::Events;
|
|
|
|
use crate::URLBundle;
|
2023-04-28 20:29:40 +02:00
|
|
|
use reqwest::Url;
|
|
|
|
use serde_json::to_string;
|
2023-04-28 18:18:32 +02:00
|
|
|
use tokio::net::TcpStream;
|
|
|
|
use tokio_tungstenite::tungstenite::Error;
|
|
|
|
use tokio_tungstenite::MaybeTlsStream;
|
|
|
|
use tokio_tungstenite::WebSocketStream;
|
2023-04-27 22:38:41 +02:00
|
|
|
|
2023-04-28 13:40:29 +02:00
|
|
|
/**
|
2023-04-28 18:18:32 +02:00
|
|
|
Represents a Gateway connection. A Gateway connection will create observable
|
|
|
|
[`GatewayEvents`](GatewayEvent), which you can subscribe to. Gateway events include all currently
|
|
|
|
implemented [Types] with the trait [`WebSocketEvent`]
|
|
|
|
*/
|
|
|
|
pub struct Gateway<'a> {
|
|
|
|
pub url: String,
|
|
|
|
pub events: Events<'a>,
|
2023-04-28 20:29:40 +02:00
|
|
|
socket: Arc<Mutex<Option<WebSocketStream<MaybeTlsStream<TcpStream>>>>>,
|
|
|
|
thread_handle: Option<JoinHandle<()>>,
|
2023-04-28 18:18:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Gateway<'a> {
|
2023-04-28 20:29:40 +02:00
|
|
|
pub async fn new(websocket_url: String, token: String) {
|
2023-04-28 18:18:32 +02:00
|
|
|
let parsed_url = Url::parse(&URLBundle::parse_url(websocket_url.clone())).unwrap();
|
|
|
|
if parsed_url.scheme() != "ws" && parsed_url.scheme() != "wss" {
|
2023-04-28 20:29:40 +02:00
|
|
|
//return Err(Error::Url(UrlError::UnsupportedUrlScheme));
|
2023-04-28 18:18:32 +02:00
|
|
|
}
|
2023-04-28 20:29:40 +02:00
|
|
|
let payload = GatewayIdentifyPayload {
|
|
|
|
token: token,
|
|
|
|
properties: GatewayIdentifyConnectionProps {
|
|
|
|
os: "any".to_string(),
|
|
|
|
browser: "chorus-polyphony".to_string(),
|
|
|
|
device: "chorus-lib".to_string(),
|
|
|
|
},
|
|
|
|
compress: Some(true),
|
|
|
|
large_threshold: None,
|
|
|
|
shard: None,
|
|
|
|
presence: None,
|
|
|
|
intents: 3276799,
|
|
|
|
};
|
|
|
|
let payload_string = to_string(&payload).unwrap();
|
2023-04-28 18:18:32 +02:00
|
|
|
}
|
|
|
|
}
|
2023-04-27 17:57:10 +02:00
|
|
|
|
2023-04-28 13:40:29 +02:00
|
|
|
/**
|
|
|
|
Trait which defines the behaviour of an Observer. An Observer is an object which is subscribed to
|
|
|
|
an Observable. The Observer is notified when the Observable's data changes.
|
2023-04-28 18:18:32 +02:00
|
|
|
In this case, the Observable is a [`GatewayEvent`], which is a wrapper around a WebSocketEvent.
|
2023-04-28 13:40:29 +02:00
|
|
|
*/
|
2023-04-28 12:31:59 +02:00
|
|
|
pub trait Observer<T: WebSocketEvent> {
|
|
|
|
fn update(&self, data: &T);
|
2023-04-27 17:57:10 +02:00
|
|
|
}
|
|
|
|
|
2023-04-28 13:40:29 +02:00
|
|
|
/** GatewayEvent is a wrapper around a WebSocketEvent. It is used to notify the observers of a
|
2023-04-28 18:18:32 +02:00
|
|
|
change in the WebSocketEvent. GatewayEvents are observable.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#[derive(Default)]
|
2023-04-28 12:31:59 +02:00
|
|
|
pub struct GatewayEvent<'a, T: WebSocketEvent> {
|
|
|
|
observers: Vec<&'a dyn Observer<T>>,
|
|
|
|
pub event_data: T,
|
2023-04-27 22:29:07 +02:00
|
|
|
pub is_observed: bool,
|
2023-04-27 17:57:10 +02:00
|
|
|
}
|
|
|
|
|
2023-04-28 12:31:59 +02:00
|
|
|
impl<'a, T: WebSocketEvent> GatewayEvent<'a, T> {
|
|
|
|
fn new(event_data: T) -> Self {
|
2023-04-27 17:57:10 +02:00
|
|
|
Self {
|
2023-04-27 22:29:07 +02:00
|
|
|
is_observed: false,
|
2023-04-27 17:57:10 +02:00
|
|
|
observers: Vec::new(),
|
2023-04-28 12:31:59 +02:00
|
|
|
event_data,
|
2023-04-27 17:57:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-28 13:40:29 +02:00
|
|
|
/**
|
|
|
|
Returns true if the GatewayEvent is observed by at least one Observer.
|
2023-04-28 18:18:32 +02:00
|
|
|
*/
|
2023-04-27 22:29:07 +02:00
|
|
|
pub fn is_observed(&self) -> bool {
|
|
|
|
self.is_observed
|
|
|
|
}
|
|
|
|
|
2023-04-28 13:40:29 +02:00
|
|
|
/**
|
|
|
|
Subscribes an Observer to the GatewayEvent. Returns an error if the GatewayEvent is already
|
|
|
|
observed.
|
|
|
|
# Errors
|
|
|
|
Returns an error if the GatewayEvent is already observed.
|
|
|
|
Error type: [`ObserverError::AlreadySubscribedError`]
|
2023-04-28 18:18:32 +02:00
|
|
|
*/
|
2023-04-28 12:31:59 +02:00
|
|
|
pub fn subscribe(&mut self, observable: &'a dyn Observer<T>) -> Option<ObserverError> {
|
2023-04-27 22:29:07 +02:00
|
|
|
if self.is_observed {
|
2023-04-27 22:38:41 +02:00
|
|
|
return Some(ObserverError::AlreadySubscribedError);
|
2023-04-27 22:29:07 +02:00
|
|
|
}
|
|
|
|
self.is_observed = true;
|
2023-04-27 22:38:41 +02:00
|
|
|
self.observers.push(observable);
|
|
|
|
None
|
2023-04-27 17:57:10 +02:00
|
|
|
}
|
|
|
|
|
2023-04-28 13:40:29 +02:00
|
|
|
/**
|
|
|
|
Unsubscribes an Observer from the GatewayEvent.
|
2023-04-28 18:18:32 +02:00
|
|
|
*/
|
2023-04-28 12:31:59 +02:00
|
|
|
pub fn unsubscribe(&mut self, observable: &'a dyn Observer<T>) {
|
|
|
|
// .retain()'s closure retains only those elements of the vector, which have a different
|
|
|
|
// pointer value than observable.
|
|
|
|
self.observers.retain(|obs| !std::ptr::eq(*obs, observable));
|
|
|
|
self.is_observed = !self.observers.is_empty();
|
2023-04-27 22:29:07 +02:00
|
|
|
return;
|
2023-04-27 17:57:10 +02:00
|
|
|
}
|
|
|
|
|
2023-04-28 13:40:29 +02:00
|
|
|
/**
|
|
|
|
Updates the GatewayEvent's data and notifies the observers.
|
2023-04-28 18:18:32 +02:00
|
|
|
*/
|
2023-04-28 12:31:59 +02:00
|
|
|
fn update_data(&mut self, new_event_data: T) {
|
|
|
|
self.event_data = new_event_data;
|
2023-04-27 17:57:10 +02:00
|
|
|
self.notify();
|
|
|
|
}
|
|
|
|
|
2023-04-28 13:40:29 +02:00
|
|
|
/**
|
|
|
|
Notifies the observers of the GatewayEvent.
|
2023-04-28 18:18:32 +02:00
|
|
|
*/
|
2023-04-27 22:29:07 +02:00
|
|
|
fn notify(&self) {
|
2023-04-27 17:57:10 +02:00
|
|
|
for observer in &self.observers {
|
2023-04-28 12:31:59 +02:00
|
|
|
observer.update(&self.event_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-28 18:18:32 +02:00
|
|
|
mod events {
|
|
|
|
use super::*;
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct Events<'a> {
|
|
|
|
pub message: Message<'a>,
|
|
|
|
pub user: User<'a>,
|
|
|
|
pub gateway_identify_payload: GatewayEvent<'a, GatewayIdentifyPayload>,
|
|
|
|
pub gateway_resume: GatewayEvent<'a, GatewayResume>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct Message<'a> {
|
|
|
|
pub create: GatewayEvent<'a, MessageCreate>,
|
|
|
|
pub update: GatewayEvent<'a, MessageUpdate>,
|
|
|
|
pub delete: GatewayEvent<'a, MessageDelete>,
|
|
|
|
pub delete_bulk: GatewayEvent<'a, MessageDeleteBulk>,
|
|
|
|
pub reaction_add: GatewayEvent<'a, MessageReactionAdd>,
|
|
|
|
pub reaction_remove: GatewayEvent<'a, MessageReactionRemove>,
|
|
|
|
pub reaction_remove_all: GatewayEvent<'a, MessageReactionRemoveAll>,
|
|
|
|
pub reaction_remove_emoji: GatewayEvent<'a, MessageReactionRemoveEmoji>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct User<'a> {
|
|
|
|
pub presence_update: GatewayEvent<'a, PresenceUpdate>,
|
|
|
|
pub typing_start_event: GatewayEvent<'a, TypingStartEvent>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-28 12:31:59 +02:00
|
|
|
#[cfg(test)]
|
2023-04-28 12:39:58 +02:00
|
|
|
mod example {
|
2023-04-28 12:31:59 +02:00
|
|
|
use super::*;
|
|
|
|
use crate::api::types::GatewayResume;
|
|
|
|
|
|
|
|
struct Consumer;
|
|
|
|
impl Observer<GatewayResume> for Consumer {
|
|
|
|
fn update(&self, data: &GatewayResume) {
|
|
|
|
println!("{}", data.token)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_observer_behaviour() {
|
|
|
|
let mut event = GatewayEvent::new(GatewayResume {
|
|
|
|
token: "start".to_string(),
|
|
|
|
session_id: "start".to_string(),
|
|
|
|
seq: "start".to_string(),
|
|
|
|
});
|
|
|
|
|
|
|
|
let new_data = GatewayResume {
|
|
|
|
token: "token_3276ha37am3".to_string(),
|
|
|
|
session_id: "89346671230".to_string(),
|
|
|
|
seq: "3".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let consumer = Consumer;
|
|
|
|
|
|
|
|
event.subscribe(&consumer);
|
|
|
|
|
|
|
|
event.notify();
|
|
|
|
|
|
|
|
event.update_data(new_data);
|
|
|
|
|
|
|
|
let second_consumer = Consumer;
|
|
|
|
|
|
|
|
match event.subscribe(&second_consumer) {
|
2023-04-28 12:39:58 +02:00
|
|
|
None => assert!(false),
|
2023-04-28 12:31:59 +02:00
|
|
|
Some(err) => println!("You cannot subscribe twice: {}", err),
|
2023-04-27 17:57:10 +02:00
|
|
|
}
|
|
|
|
}
|
2023-04-25 17:21:27 +02:00
|
|
|
}
|