pub mod types;
use std::sync::Arc;
use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures::{SinkExt, StreamExt};
use tokio::spawn;
use tokio::sync::Mutex;
use self::types::{ShardManagerMessage, ShardRunnerInfo};
use crate::errors::{Error, Result};
use crate::leap::types::Event;
use crate::messenger::types::ShardMessengerMessage;
use crate::messenger::ShardMessenger;
use crate::runner::ShardRunner;
use crate::shard::error::Error as GatewayError;
use crate::shard::types::InterMessage;
use crate::shard::Shard;
#[derive(Debug)]
pub struct ManagerOptions<'a> {
pub project: &'a str,
pub ws_url: &'a str,
pub token: Option<&'a str>,
pub event_tx: UnboundedSender<Event>,
}
#[derive(Debug)]
struct RunnerOptions<'a> {
ws_url: Arc<Mutex<String>>,
project: &'a str,
token: Option<&'a str>,
manager_tx: UnboundedSender<ShardManagerMessage>,
}
#[derive(Debug)]
pub struct ShardManager {
pub runner_info: Arc<Mutex<ShardRunnerInfo>>,
manager_rx: UnboundedReceiver<ShardManagerMessage>,
manager_tx: UnboundedSender<ShardManagerMessage>,
event_tx: UnboundedSender<Event>,
messenger_tx: UnboundedSender<ShardMessengerMessage>,
token: Option<String>,
project: String,
ws_url: Arc<Mutex<String>>,
}
impl ShardManager {
pub async fn new(options: ManagerOptions<'_>) -> Result<Self> {
let (manager_tx, manager_rx) = unbounded();
let ws_url = Arc::new(Mutex::new(options.ws_url.to_string()));
let runner_info = Self::create_runner(&RunnerOptions {
ws_url: ws_url.clone(),
manager_tx: manager_tx.clone(),
project: options.project,
token: options.token,
})
.await?;
let mut messenger = ShardMessenger::new(runner_info.clone()).await;
let messenger_tx = messenger.get_tx();
spawn(async move {
if let Err(why) = messenger.run().await {
log::debug!("[Messenger] Stopped: {why:?}");
} else {
log::debug!("[Messenge] Stopped");
}
});
Ok(Self {
ws_url,
manager_rx,
manager_tx,
messenger_tx,
event_tx: options.event_tx,
project: options.project.to_string(),
token: options.token.map(std::string::ToString::to_string),
runner_info,
})
}
pub async fn run(&mut self) -> Result<()> {
loop {
match self.manager_rx.next().await {
Some(ShardManagerMessage::Event(data)) => {
self.event_tx.send(data).await.ok();
}
Some(ShardManagerMessage::Json(data)) => {
self.messenger_tx
.send(ShardMessengerMessage::Json(data))
.await
.ok();
}
Some(ShardManagerMessage::Restart) => {
self.runner_info = Self::create_runner(&RunnerOptions {
ws_url: self.ws_url.clone(),
manager_tx: self.manager_tx.clone(),
project: &self.project,
token: self.token.as_deref(),
})
.await?;
}
Some(ShardManagerMessage::Update(data)) => {
self.messenger_tx
.send(ShardMessengerMessage::Update(data.clone()))
.await
.ok();
let runner = ShardRunnerInfo {
latency: data.latency,
runner_tx: self.runner_info.lock().await.runner_tx.clone(),
stage: data.stage,
};
self.runner_info = Arc::new(Mutex::new(runner));
}
Some(ShardManagerMessage::Close) => {
self.shutdown().await;
return Ok(());
}
Some(ShardManagerMessage::InvalidAuthentication) => {
self.shutdown().await;
return Err(Error::Gateway(GatewayError::InvalidAuthentication));
}
None => {
return Ok(());
}
}
}
}
async fn create_runner(options: &RunnerOptions<'_>) -> Result<Arc<Mutex<ShardRunnerInfo>>> {
let shard = Shard::new(options.ws_url.clone(), options.project, options.token).await?;
let mut runner = ShardRunner::new(options.manager_tx.clone(), shard);
let stage = runner.shard.stage();
let runner_tx = runner.get_tx();
spawn(async move {
if let Err(why) = runner.run().await {
log::debug!("[Shard] Runner error: {:?}", why);
}
log::debug!("[Shard] Stopped");
});
Ok(Arc::new(Mutex::new(ShardRunnerInfo {
latency: None,
runner_tx,
stage,
})))
}
async fn shutdown(&mut self) {
self.messenger_tx
.send(ShardMessengerMessage::Close)
.await
.ok();
self.runner_info
.lock()
.await
.runner_tx
.send(InterMessage::Close)
.await
.ok();
}
pub fn get_manager_tx(&self) -> UnboundedSender<ShardManagerMessage> {
self.manager_tx.clone()
}
}