From 4d14d2ddf5df375a2eaef01cc9c6f246ec463923 Mon Sep 17 00:00:00 2001 From: Claas Date: Sat, 2 Aug 2025 16:11:48 +0200 Subject: [PATCH] Move towards publish trait for loose coupling Remove premature abstraction Encode trait that has only one implementation and stood in the way of blanket implementation for publish trait --- fan-controller/src/mqtt/mod.rs | 65 +++++++++++++++++-- .../src/mqtt/packet/ping_request.rs | 11 +++- fan-controller/src/mqtt/task.rs | 2 +- fan-controller/src/task.rs | 3 +- 4 files changed, 69 insertions(+), 12 deletions(-) diff --git a/fan-controller/src/mqtt/mod.rs b/fan-controller/src/mqtt/mod.rs index 94bd3bd..db6197f 100644 --- a/fan-controller/src/mqtt/mod.rs +++ b/fan-controller/src/mqtt/mod.rs @@ -39,20 +39,69 @@ pub(super) enum ConnectErrorReasonCode { #[derive(Debug, Clone, Format)] pub struct UnknownConnectErrorReasonCode(u8); -pub(crate) trait Encode { - fn encode(&self, buffer: &mut [u8], offset: &mut usize); -} - pub(crate) trait TryEncode { type Error; fn try_encode(&self, buffer: &mut [u8], offset: &mut usize) -> Result<(), Self::Error>; } -impl TryEncode for T { - type Error = Infallible; +impl TryEncode for T { + type Error = publish::EncodeError; fn try_encode(&self, buffer: &mut [u8], offset: &mut usize) -> Result<(), Self::Error> { - self.encode(buffer, offset); + if buffer.is_empty() { + return Err(publish::EncodeError::EmptyBuffer); + } + + // Fixed header + //TODO set flags + buffer[*offset] = Self::TYPE << 4; + *offset += 1; + + let topic_name = self.topic(); + // Remaining length + let topic_name_length = topic_name.len(); + let variable_header_length = size_of::() + topic_name_length + size_of::(); + let payload = self.payload(); + let remaining_length = variable_header_length + payload.len(); + + let length_length = variable_byte_integer::encode(remaining_length, buffer, offset) + .map_err(publish::EncodeError::VariableByteIntegerError)?; + + let required_length = size_of_val(&Self::TYPE) + length_length + remaining_length; + + if required_length > buffer.len() - *offset { + return Err(publish::EncodeError::BufferTooSmall { + required: required_length, + available: buffer.len() - *offset, + }); + } + + // Variable header + // Topic name length + buffer[*offset] = (topic_name_length >> 8) as u8; + *offset += 1; + buffer[*offset] = topic_name_length as u8; + *offset += 1; + // Topic name + for byte in topic_name.as_bytes() { + buffer[*offset] = *byte; + *offset += 1; + } + + // Property length + // No properties supported for now so set to 0 + buffer[*offset] = 0; + *offset += 1; + + // Payload + // No need to set length as it will be calculated + for byte in payload { + buffer[*offset] = *byte; + *offset += 1; + } + + assert_eq!(required_length, buffer[..*offset].len()); + Ok(()) } } @@ -104,3 +153,5 @@ macro_rules! non_zero_u16 { } pub(crate) use non_zero_u16; + +use crate::mqtt::packet::publish; diff --git a/fan-controller/src/mqtt/packet/ping_request.rs b/fan-controller/src/mqtt/packet/ping_request.rs index a70904e..12b8aeb 100644 --- a/fan-controller/src/mqtt/packet/ping_request.rs +++ b/fan-controller/src/mqtt/packet/ping_request.rs @@ -1,4 +1,6 @@ -use crate::mqtt::Encode; +use core::convert::Infallible; + +use crate::mqtt::TryEncode; pub(crate) struct PingRequest; @@ -6,12 +8,15 @@ impl PingRequest { pub(crate) const TYPE: u8 = 12; } -impl Encode for PingRequest { - fn encode(&self, buffer: &mut [u8], offset: &mut usize) { +impl TryEncode for PingRequest { + type Error = Infallible; + + fn try_encode(&self, buffer: &mut [u8], offset: &mut usize) -> Result<(), Self::Error> { buffer[*offset] = Self::TYPE << 4; *offset += 1; // Setting it to 0, because this might not be 0 if we reuse the buffer buffer[*offset] = 0; *offset += 1; + Ok(()) } } diff --git a/fan-controller/src/mqtt/task.rs b/fan-controller/src/mqtt/task.rs index 374e25d..c0ee4e2 100644 --- a/fan-controller/src/mqtt/task.rs +++ b/fan-controller/src/mqtt/task.rs @@ -20,7 +20,7 @@ pub(crate) enum SendError { Flush(tcp::Error), } -pub(crate) async fn send<'a, T>( +pub(crate) async fn send( socket: &mut impl Write, packet: T, ) -> Result<(), SendError<::Error>> diff --git a/fan-controller/src/task.rs b/fan-controller/src/task.rs index e82ae67..86ca622 100644 --- a/fan-controller/src/task.rs +++ b/fan-controller/src/task.rs @@ -5,11 +5,11 @@ use crate::mqtt::packet::publish; use crate::mqtt::packet::subscribe::{Subscribe, Subscription}; use crate::mqtt::packet::subscribe_acknowledgement::SubscribeAcknowledgement; use crate::mqtt::task::send; +use crate::mqtt::{self, TryEncode}; use crate::mqtt::{non_zero_u16, TryDecode}; use crate::Fans; use crate::PingRequest; use crate::{configuration, fan, gain_control, FanState}; -use crate::mqtt; use ::mqtt::QualityOfService; use core::future::poll_fn; use core::num::NonZeroU16; @@ -575,6 +575,7 @@ const SUBSCRIPTIONS: [Subscription; 2] = [ /// Trait must be implemented by types that represent messages that can be published to MQTT. pub(super) trait Publish { + const TYPE: u8 = 3; fn topic(&self) -> &str; fn payload(&self) -> &[u8]; } -- 2.54.0