use crate::commands::auth::AuthCommand;
use crate::commands::builder::{CommandBuilder, ToStringOption};
use crate::commands::hello::HelloCommand;
use crate::commands::ping::PingCommand;
use crate::commands::Command;
use crate::network::buffer::Network;
use crate::network::client::{Client, CommandErrors};
use crate::network::handler::ConnectionError::{TcpConnectionFailed, TcpSocketError};
use crate::network::protocol::{Protocol, Resp2, Resp3};
use crate::network::response::MemoryParameters;
use alloc::string::{String, ToString};
use core::cell::RefCell;
use embedded_nal::{SocketAddr, TcpClientStack};
use embedded_time::duration::Extensions;
use embedded_time::duration::Microseconds;
use embedded_time::Clock;
#[derive(Debug, Eq, PartialEq)]
pub enum ConnectionError {
TcpSocketError,
TcpConnectionFailed,
AuthenticationError(CommandErrors),
ProtocolSwitchError(CommandErrors),
}
#[derive(Clone)]
pub struct Credentials {
pub(crate) username: Option<String>,
pub(crate) password: String,
}
impl Credentials {
pub fn acl(username: &str, password: &str) -> Self {
Credentials {
username: Some(username.to_string()),
password: password.to_string(),
}
}
pub fn password_only(password: &str) -> Self {
Self {
username: None,
password: password.to_string(),
}
}
}
pub struct ConnectionHandler<N: TcpClientStack, P: Protocol>
where
HelloCommand: Command<<P as Protocol>::FrameType>,
{
remote: SocketAddr,
authentication: Option<Credentials>,
socket: Option<N::TcpSocket>,
auth_failed: bool,
timeout: Microseconds,
memory: MemoryParameters,
protocol: P,
use_ping: bool,
pub(crate) hello_response: Option<<HelloCommand as Command<<P as Protocol>::FrameType>>::Response>,
}
impl<N: TcpClientStack> ConnectionHandler<N, Resp2> {
pub fn resp2(remote: SocketAddr) -> ConnectionHandler<N, Resp2> {
ConnectionHandler::new(remote, Resp2 {})
}
}
impl<N: TcpClientStack> ConnectionHandler<N, Resp3> {
pub fn resp3(remote: SocketAddr) -> ConnectionHandler<N, Resp3> {
ConnectionHandler::new(remote, Resp3 {})
}
}
impl<N: TcpClientStack, P: Protocol> ConnectionHandler<N, P>
where
AuthCommand: Command<<P as Protocol>::FrameType>,
HelloCommand: Command<<P as Protocol>::FrameType>,
PingCommand: Command<<P as Protocol>::FrameType>,
<P as Protocol>::FrameType: ToStringOption,
<P as Protocol>::FrameType: From<CommandBuilder>,
{
fn new(remote: SocketAddr, protocol: P) -> Self {
ConnectionHandler {
remote,
authentication: None,
socket: None,
auth_failed: false,
timeout: 0.microseconds(),
memory: MemoryParameters::default(),
protocol,
use_ping: false,
hello_response: None,
}
}
pub fn connect<'a, C: Clock>(
&'a mut self,
network: &'a mut N,
clock: Option<&'a C>,
) -> Result<Client<'a, N, C, P>, ConnectionError> {
if self.auth_failed {
self.disconnect(network);
}
self.test_socket(network, clock);
if self.socket.is_some() {
return Ok(self.create_client(network, clock));
}
self.new_client(network, clock)
}
fn new_client<'a, C: Clock>(
&'a mut self,
network: &'a mut N,
clock: Option<&'a C>,
) -> Result<Client<'a, N, C, P>, ConnectionError> {
self.connect_socket(network)?;
let credentials = self.authentication.clone();
let client = self.create_client(network, clock);
match client.init(credentials) {
Ok(response) => {
self.hello_response = response;
Ok(self.create_client(network, clock))
}
Err(error) => {
self.auth_failed = true;
Err(error)
}
}
}
fn test_socket<'a, C: Clock>(&'a mut self, network: &'a mut N, clock: Option<&'a C>) {
if self.socket.is_none() {
return;
}
if self.use_ping && self.ping(network, clock).is_err() {
self.disconnect(network);
}
}
fn ping<'a, C: Clock>(
&'a mut self,
network: &'a mut N,
clock: Option<&'a C>,
) -> Result<(), CommandErrors> {
self.create_client(network, clock).ping()?.wait()?;
Ok(())
}
pub fn disconnect(&mut self, network: &mut N) {
if self.socket.is_none() {
return;
}
let _ = network.close(self.socket.take().unwrap());
self.auth_failed = false;
}
fn connect_socket(&mut self, network: &mut N) -> Result<(), ConnectionError> {
let socket_result = network.socket();
if socket_result.is_err() {
return Err(TcpSocketError);
}
let mut socket = socket_result.unwrap();
if network.connect(&mut socket, self.remote).is_err() {
let _ = network.close(socket);
return Err(TcpConnectionFailed);
};
self.socket = Some(socket);
Ok(())
}
fn create_client<'a, C: Clock>(
&'a mut self,
stack: &'a mut N,
clock: Option<&'a C>,
) -> Client<'a, N, C, P> {
Client {
network: Network::new(
RefCell::new(stack),
RefCell::new(self.socket.as_mut().unwrap()),
self.protocol.clone(),
self.memory.clone(),
),
timeout_duration: self.timeout,
clock,
hello_response: self.hello_response.as_ref(),
}
}
}
impl<N: TcpClientStack, P: Protocol> ConnectionHandler<N, P>
where
HelloCommand: Command<<P as Protocol>::FrameType>,
{
pub fn timeout(&mut self, timeout: Microseconds) -> &mut Self {
self.timeout = timeout;
self
}
pub fn auth(&mut self, credentials: Credentials) -> &mut Self {
self.authentication = Some(credentials);
self
}
pub fn use_ping(&mut self) -> &mut Self {
self.use_ping = true;
self
}
pub fn memory(&mut self, parameters: MemoryParameters) -> &mut Self {
self.memory = parameters;
self
}
}