use std::time::Duration;
use tokio::{
io::{AsyncRead, AsyncWrite},
sync::oneshot::Sender,
time,
};
use tokio_tungstenite::{connect_async, tungstenite::protocol::WebSocketConfig};
use crate::{
apperror::AppError,
rules::Rule,
session::{LoginInfo, Session, start},
};
pub fn get_default_hostname() -> &'static str {
"access.websof.de"
}
pub fn get_url(hostname: &str, tls: bool, port: Option<u16>) -> String {
let scheme = if tls { "wss://" } else { "ws://" };
let port = port.unwrap_or(if tls { 443 } else { 80 });
String::new() + scheme + hostname + ":" + &port.to_string() + "/api/websocket"
}
pub struct Builder {
token: String,
client_name: Option<String>,
client_id: Option<String>,
mac: Option<String>,
rule: Rule,
tls: bool,
event_poll_interval: Duration,
handshake_timeout: Duration,
on_session: Option<Sender<Session>>,
websocket_config: WebSocketConfig,
hostname: String,
port: Option<u16>,
}
impl Builder {
pub fn new<T: ToString>(token: &T) -> Self {
Builder {
token: token.to_string(),
client_name: None,
client_id: None,
mac: None,
rule: Rule::unrestricted(),
tls: true,
event_poll_interval: Duration::from_secs(1),
handshake_timeout: Duration::from_secs(60),
on_session: None,
websocket_config: WebSocketConfig::default(),
hostname: get_default_hostname().to_string(),
port: None,
}
}
pub fn with_client_name<T: ToString>(mut self, client_name: &T) -> Self {
self.client_name = Some(client_name.to_string());
self
}
pub fn with_client_name_opt(mut self, client_name: &Option<String>) -> Self {
self.client_name = client_name.clone();
self
}
pub fn with_client_id<T: ToString>(mut self, client_id: &T) -> Self {
self.client_id = Some(client_id.to_string());
self
}
pub fn with_client_id_opt(mut self, client_id: &Option<String>) -> Self {
self.client_id = client_id.clone();
self
}
pub fn with_rule(mut self, rule: Rule) -> Self {
self.rule = rule;
self
}
pub fn with_tls(mut self, tls: bool) -> Self {
self.tls = tls;
self
}
pub fn with_mac<T: ToString>(mut self, mac: &T) -> Self {
self.mac = Some(mac.to_string());
self
}
pub fn with_event_poll_interval(mut self, event_poll_interval: Duration) -> Self {
self.event_poll_interval = event_poll_interval;
self
}
pub fn with_handshake_timeout(mut self, handshake_timeout: Duration) -> Self {
self.handshake_timeout = handshake_timeout;
self
}
pub fn with_on_session(mut self, on_session: Sender<Session>) -> Self {
self.on_session = Some(on_session);
self
}
pub fn with_write_buffer_size(mut self, write_buffer_size: usize) -> Self {
self.websocket_config = self.websocket_config.write_buffer_size(write_buffer_size);
self
}
pub fn with_read_buffer_size(mut self, read_buffer_size: usize) -> Self {
self.websocket_config = self.websocket_config.read_buffer_size(read_buffer_size);
self
}
pub fn with_max_message_size(mut self, max_message_size: usize) -> Self {
self.websocket_config = self
.websocket_config
.max_message_size(Some(max_message_size));
self
}
pub fn with_max_frame_size(mut self, max_frame_size: usize) -> Self {
self.websocket_config = self.websocket_config.max_frame_size(Some(max_frame_size));
self
}
pub fn with_hostname(mut self, hostname: &str) -> Self {
self.hostname = hostname.to_string();
self
}
pub fn with_port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
pub fn get_url(&self) -> String {
get_url(&self.hostname, self.tls, self.port)
}
pub async fn start_connected<S: AsyncRead + AsyncWrite + Unpin>(
self,
stream: S,
) -> Result<(), AppError> {
let (socket, _) = time::timeout(
self.handshake_timeout,
tokio_tungstenite::client_async_with_config(
get_url(&self.hostname, self.tls, self.port),
stream,
Some(self.websocket_config),
),
)
.await
.map_err(|_| AppError::new("timeout while handshaking"))?
.map_err(|_| AppError::new("failed to create client async"))?;
start(
LoginInfo::new(self.token, self.client_name, self.client_id, self.mac),
self.rule,
socket,
self.event_poll_interval,
self.on_session,
)
.await
}
pub async fn connect(self) -> Result<(), AppError> {
let (socket, _) = connect_async(&self.get_url())
.await
.map_err(|e| AppError::new(format!("Can't connect: {e}")))?;
start(
LoginInfo::new(self.token, self.client_name, self.client_id, self.mac),
self.rule,
socket,
self.event_poll_interval,
self.on_session,
)
.await
}
}