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;
use crate::network::protocol::{Protocol, Resp2, Resp3};
use crate::network::response::MemoryParameters;
use alloc::string::{String, ToString};
use core::marker::PhantomData;
use core::net::SocketAddr;
use embedded_nal_async::TcpConnect;
use embedded_time::duration::Extensions;
use embedded_time::duration::Microseconds;
use embedded_time::Clock;
#[derive(Debug, Eq, PartialEq)]
pub enum ConnectionError {
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: TcpConnect, P: Protocol>
where
HelloCommand: Command<<P as Protocol>::FrameType>,
{
remote: SocketAddr,
authentication: Option<Credentials>,
timeout: Microseconds,
memory: MemoryParameters,
protocol: P,
use_ping: bool,
network: PhantomData<N>,
}
impl<N: TcpConnect> ConnectionHandler<N, Resp2> {
pub fn resp2(remote: SocketAddr) -> Self {
ConnectionHandler::new(remote, Resp2 {})
}
}
impl<N: TcpConnect> ConnectionHandler<N, Resp3> {
pub fn resp3(remote: SocketAddr) -> Self {
ConnectionHandler::new(remote, Resp3 {})
}
}
impl<N: TcpConnect, 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,
timeout: 0.microseconds(),
memory: MemoryParameters::default(),
protocol,
use_ping: false,
network: PhantomData,
}
}
pub async fn connect<'a, C: Clock>(
&self,
network: &'a N,
clock: Option<&'a C>,
) -> Result<Client<'a, N::Connection<'a>, C, P>, ConnectionError> {
let connection = network.connect(self.remote).await.map_err(|_| TcpConnectionFailed)?;
let mut client = Client {
network: Network::new(connection, self.protocol.clone(), self.memory.clone()),
timeout_duration: self.timeout,
clock,
hello_response: None,
};
client.hello_response = client.init(self.authentication.clone()).await?;
if self.use_ping {
client.ping().await.map_err(|_| TcpConnectionFailed)?;
}
Ok(client)
}
}
impl<N: TcpConnect, 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
}
}