use crate::{ConfigError, Will, types::Auth};
use embassy_time::Duration;
use heapless::String;
#[derive(Debug)]
pub struct Buffers<'a> {
rx: &'a mut [u8],
tx: &'a mut [u8],
}
impl<'a> Buffers<'a> {
pub const fn new(rx: &'a mut [u8], tx: &'a mut [u8]) -> Self {
Self { rx, tx }
}
pub const fn rx(&self) -> &[u8] {
self.rx
}
pub const fn tx(&self) -> &[u8] {
self.tx
}
pub(crate) fn into_parts(self) -> (&'a mut [u8], &'a mut [u8]) {
(self.rx, self.tx)
}
pub fn split(buffer: &'a mut [u8], rx_size: usize) -> Result<Self, ConfigError> {
if rx_size > buffer.len() {
return Err(ConfigError::BufferSplit);
}
let (rx, tx) = buffer.split_at_mut(rx_size);
Ok(Self::new(rx, tx))
}
}
#[derive(Debug)]
pub struct ConfigBuilder<'a> {
buffers: Buffers<'a>,
will: Option<Will<'a>>,
client_id: String<64>,
keepalive_interval: Duration,
session_expiry_interval: u32,
downgrade_qos: bool,
auth: Option<Auth<'a>>,
}
impl<'a> ConfigBuilder<'a> {
pub fn new(buffers: Buffers<'a>) -> Self {
Self {
buffers,
will: None,
client_id: String::new(),
auth: None,
keepalive_interval: Duration::from_secs(60),
session_expiry_interval: 0,
downgrade_qos: false,
}
}
pub fn from_buffer(buffer: &'a mut [u8], rx_size: usize) -> Result<Self, ConfigError> {
Ok(Self::new(Buffers::split(buffer, rx_size)?))
}
pub fn auth(mut self, user_name: &'a str, password: &'a [u8]) -> Result<Self, ConfigError> {
if self.auth.is_some() {
return Err(ConfigError::DuplicateConfig);
}
self.auth.replace(Auth::new(user_name, password));
Ok(self)
}
pub fn client_id(mut self, id: &str) -> Result<Self, ConfigError> {
self.client_id = id.try_into().map_err(|_| ConfigError::ClientIdTooLong)?;
Ok(self)
}
pub fn keepalive_interval(mut self, seconds: u16) -> Self {
self.keepalive_interval = Duration::from_secs(seconds as u64);
self
}
pub fn session_expiry_interval(mut self, seconds: u32) -> Self {
self.session_expiry_interval = seconds;
self
}
pub fn autodowngrade_qos(mut self) -> Self {
self.downgrade_qos = true;
self
}
pub const fn rx_len(&self) -> usize {
self.buffers.rx().len()
}
pub const fn tx_len(&self) -> usize {
self.buffers.tx().len()
}
pub fn will(mut self, will: Will<'a>) -> Result<Self, ConfigError> {
if self.will.is_some() {
return Err(ConfigError::DuplicateConfig);
}
self.will = Some(will);
Ok(self)
}
pub(crate) fn into_parts(
self,
) -> (
Buffers<'a>,
Option<Will<'a>>,
String<64>,
Duration,
u32,
bool,
Option<Auth<'a>>,
) {
(
self.buffers,
self.will,
self.client_id,
self.keepalive_interval,
self.session_expiry_interval,
self.downgrade_qos,
self.auth,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_buffer() {
let mut buffer = [0; 30];
let buffers = Buffers::split(&mut buffer, 10).unwrap();
assert_eq!(buffers.rx.len(), 10);
assert_eq!(buffers.tx.len(), 20);
}
#[test]
fn split_too_large() {
let mut buffer = [0; 30];
assert!(matches!(
Buffers::split(&mut buffer, 31),
Err(ConfigError::BufferSplit)
));
}
#[test]
fn builder() {
let mut rx = [0; 10];
let mut tx = [0; 20];
let (buffers, will, client_id, _, _, _, auth) = ConfigBuilder::new(Buffers {
rx: &mut rx,
tx: &mut tx,
})
.into_parts();
assert_eq!(buffers.rx.len(), 10);
assert_eq!(buffers.tx.len(), 20);
assert!(will.is_none());
assert!(client_id.is_empty());
assert!(auth.is_none());
}
#[test]
fn will_does_not_consume_tx_buffer() {
let mut rx = [0; 10];
let mut tx = [0; 20];
let will = Will::new("topic".try_into().unwrap(), b"x", &[]).unwrap();
let (buffers, _, _, _, _, _, _) = ConfigBuilder::new(Buffers {
rx: &mut rx,
tx: &mut tx,
})
.will(will)
.unwrap()
.into_parts();
assert_eq!(buffers.tx.len(), 20);
}
}