diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71c2a0e..fac5de4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,3 +48,5 @@ jobs: run: cargo build --no-default-features --features embassy,defmt - name: Build | Examples run: cargo build --examples --features log + - name: Build | Examples - defmt + run: export DEFMT_LOG=trace; cargo check --examples --features std,defmt diff --git a/Cargo.toml b/Cargo.toml index eb3ec2f..7d6796b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,47 +50,47 @@ async-compat = "0.2" # For the `mqtt_client` example [[example]] name = "captive_portal" -required-features = ["std", "log"] +required-features = ["std"] [[example]] name = "dhcp_client" -required-features = ["std", "log"] +required-features = ["std"] [[example]] name = "dhcp_server" -required-features = ["std", "log"] +required-features = ["std"] [[example]] name = "http_client" -required-features = ["std", "log"] +required-features = ["std"] [[example]] name = "http_server" -required-features = ["std", "log"] +required-features = ["std"] [[example]] name = "mdns_responder" -required-features = ["std", "log"] +required-features = ["std"] [[example]] name = "mdns_service_responder" -required-features = ["std", "log"] +required-features = ["std"] [[example]] name = "ws_client" -required-features = ["std", "log"] +required-features = ["std"] [[example]] name = "ws_server" -required-features = ["std", "log"] +required-features = ["std"] [[example]] name = "nal_std" -required-features = ["std", "log"] +required-features = ["std"] [[example]] name = "mqtt_client" -required-features = ["std", "embedded-svc", "log"] +required-features = ["std", "embedded-svc"] [workspace] members = [ diff --git a/edge-captive/src/lib.rs b/edge-captive/src/lib.rs index 3bf8412..d8bb48e 100644 --- a/edge-captive/src/lib.rs +++ b/edge-captive/src/lib.rs @@ -87,7 +87,10 @@ pub fn reply( let buf = Buf(buf, 0); let message = domain::base::Message::from_octets(request)?; - debug!("Processing message with header: {:?}", message.header()); + debug!( + "Processing message with header: {:?}", + debug2format!(message.header()) + ); let mut responseb = domain::base::MessageBuilder::from_target(buf)?; @@ -106,10 +109,17 @@ pub fn reply( Ttl::from_duration_lossy(ttl), A::from_octets(ip[0], ip[1], ip[2], ip[3]), ); - debug!("Answering {:?} with {:?}", question, record); + debug!( + "Answering {:?} with {:?}", + debug2format!(question), + debug2format!(record) + ); answerb.push(record)?; } else { - debug!("Question {:?} is not of type A, not answering", question); + debug!( + "Question {:?} is not of type A, not answering", + debug2format!(question) + ); } } diff --git a/edge-http/src/io.rs b/edge-http/src/io.rs index 3fef17e..c2f4b2a 100644 --- a/edge-http/src/io.rs +++ b/edge-http/src/io.rs @@ -116,6 +116,29 @@ where } } +#[cfg(feature = "defmt")] +impl defmt::Format for Error +where + E: defmt::Format, +{ + fn format(&self, f: defmt::Formatter<'_>) { + match self { + Self::InvalidHeaders => defmt::write!(f, "Invalid HTTP headers or status line"), + Self::InvalidBody => defmt::write!(f, "Invalid HTTP body"), + Self::TooManyHeaders => defmt::write!(f, "Too many HTTP headers"), + Self::TooLongHeaders => defmt::write!(f, "HTTP headers section is too long"), + Self::TooLongBody => defmt::write!(f, "HTTP body is too long"), + Self::IncompleteHeaders => defmt::write!(f, "HTTP headers section is incomplete"), + Self::IncompleteBody => defmt::write!(f, "HTTP body is incomplete"), + Self::InvalidState => defmt::write!(f, "Connection is not in requested state"), + Self::HeadersMismatchError(e) => defmt::write!(f, "Headers mismatch: {}", e), + Self::WsUpgradeError(e) => defmt::write!(f, "WebSocket upgrade error: {}", e), + Self::ConnectionClosed => defmt::write!(f, "Connection closed"), + Self::Io(e) => defmt::write!(f, "{}", e), + } + } +} + #[cfg(feature = "std")] impl std::error::Error for Error where E: std::error::Error {} diff --git a/edge-http/src/io/server.rs b/edge-http/src/io/server.rs index b89057d..6c5e55d 100644 --- a/edge-http/src/io/server.rs +++ b/edge-http/src/io/server.rs @@ -332,6 +332,7 @@ where } #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum HandlerError { Io(T), Connection(Error), @@ -466,7 +467,10 @@ pub async fn handle_connection( T: Read + Write + Readable + TcpSplit + TcpShutdown, { let close = loop { - debug!("Handler task {}: Waiting for a new request", task_id); + debug!( + "Handler task {}: Waiting for a new request", + display2format!(task_id) + ); if let Some(keepalive_timeout_ms) = keepalive_timeout_ms { let wait_data = with_timeout(keepalive_timeout_ms, io.readable()).await; @@ -474,14 +478,15 @@ pub async fn handle_connection( Err(WithTimeoutError::Timeout) => { info!( "Handler task {}: Closing connection due to inactivity", - task_id + display2format!(task_id) ); break true; } Err(e) => { warn!( "Handler task {}: Error when handling request: {:?}", - task_id, e + display2format!(task_id), + debug2format!(e) ); break true; } @@ -493,13 +498,17 @@ pub async fn handle_connection( match result { Err(HandlerError::Connection(Error::ConnectionClosed)) => { - debug!("Handler task {}: Connection closed", task_id); + debug!( + "Handler task {}: Connection closed", + display2format!(task_id) + ); break false; } Err(e) => { warn!( "Handler task {}: Error when handling request: {:?}", - task_id, e + display2format!(task_id), + debug2format!(e) ); break true; } @@ -507,11 +516,14 @@ pub async fn handle_connection( if needs_close { debug!( "Handler task {}: Request complete; closing connection", - task_id + display2format!(task_id) ); break true; } else { - debug!("Handler task {}: Request complete", task_id); + debug!( + "Handler task {}: Request complete", + display2format!(task_id) + ); } } } @@ -521,7 +533,8 @@ pub async fn handle_connection( if let Err(e) = io.close(Close::Both).await { warn!( "Handler task {}: Error when closing the socket: {:?}", - task_id, e + display2format!(task_id), + debug2format!(e) ); } } else { @@ -557,6 +570,20 @@ where } } +#[cfg(feature = "defmt")] +impl defmt::Format for HandleRequestError +where + C: defmt::Format, + E: defmt::Format, +{ + fn format(&self, f: defmt::Formatter<'_>) { + match self { + Self::Connection(e) => defmt::write!(f, "Connection error: {}", e), + Self::Handler(e) => defmt::write!(f, "Handler error: {}", e), + } + } +} + impl embedded_io_async::Error for HandleRequestError where C: Debug + embedded_io_async::Error, @@ -690,7 +717,10 @@ impl Server { unwrap!(tasks .push(async move { loop { - debug!("Handler task {}: Waiting for connection", task_id); + debug!( + "Handler task {}: Waiting for connection", + display2format!(task_id) + ); let io = { let _guard = mutex.lock().await; @@ -698,7 +728,10 @@ impl Server { acceptor.accept().await.map_err(Error::Io)?.1 }; - debug!("Handler task {}: Got connection request", task_id); + debug!( + "Handler task {}: Got connection request", + display2format!(task_id) + ); handle_connection::<_, _, N>( io, @@ -715,7 +748,10 @@ impl Server { let (result, _) = embassy_futures::select::select_slice(&mut tasks).await; - warn!("Server processing loop quit abruptly: {:?}", result); + warn!( + "Server processing loop quit abruptly: {:?}", + debug2format!(result) + ); result } diff --git a/edge-http/src/lib.rs b/edge-http/src/lib.rs index f99aefa..0083c18 100644 --- a/edge-http/src/lib.rs +++ b/edge-http/src/lib.rs @@ -45,6 +45,19 @@ impl Display for HeadersMismatchError { } } +#[cfg(feature = "defmt")] +impl defmt::Format for HeadersMismatchError { + fn format(&self, f: defmt::Formatter<'_>) { + match self { + Self::ResponseConnectionTypeMismatchError => defmt::write!( + f, + "Response connection type is different from the request connection type" + ), + Self::BodyTypeError(s) => defmt::write!(f, "Body type mismatch: {}", s), + } + } +} + /// Http methods #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Hash))] @@ -202,6 +215,13 @@ impl Display for Method { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Method { + fn format(&self, f: defmt::Formatter<'_>) { + defmt::write!(f, "{}", self.as_str()) + } +} + /// HTTP headers #[derive(Debug)] pub struct Headers<'b, const N: usize = 64>([httparse::Header<'b>; N]); @@ -565,6 +585,17 @@ impl Display for ConnectionType { } } +#[cfg(feature = "defmt")] +impl defmt::Format for ConnectionType { + fn format(&self, f: defmt::Formatter<'_>) { + match self { + Self::KeepAlive => defmt::write!(f, "Keep-Alive"), + Self::Close => defmt::write!(f, "Close"), + Self::Upgrade => defmt::write!(f, "Upgrade"), + } + } +} + /// Body type #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub enum BodyType { @@ -734,6 +765,17 @@ impl Display for BodyType { } } +#[cfg(feature = "defmt")] +impl defmt::Format for BodyType { + fn format(&self, f: defmt::Formatter<'_>) { + match self { + Self::Chunked => defmt::write!(f, "Chunked"), + Self::ContentLen(len) => defmt::write!(f, "Content-Length: {}", len), + Self::Raw => defmt::write!(f, "Raw"), + } + } +} + /// Request headers including the request line (method, path) #[derive(Debug)] pub struct RequestHeaders<'b, const N: usize> { @@ -790,6 +832,23 @@ impl Display for RequestHeaders<'_, N> { } } +#[cfg(feature = "defmt")] +impl defmt::Format for RequestHeaders<'_, N> { + fn format(&self, f: defmt::Formatter<'_>) { + defmt::write!(f, "{} ", if self.http11 { "HTTP/1.1" } else { "HTTP/1.0" }); + + defmt::write!(f, "{} {}\n", self.method, self.path); + + for (name, value) in self.headers.iter() { + if name.is_empty() { + break; + } + + defmt::write!(f, "{}: {}\n", name, value); + } + } +} + /// Response headers including the response line (HTTP version, status code, reason phrase) #[derive(Debug)] pub struct ResponseHeaders<'b, const N: usize> { @@ -851,6 +910,23 @@ impl Display for ResponseHeaders<'_, N> { } } +#[cfg(feature = "defmt")] +impl defmt::Format for ResponseHeaders<'_, N> { + fn format(&self, f: defmt::Formatter<'_>) { + defmt::write!(f, "{} ", if self.http11 { "HTTP/1.1 " } else { "HTTP/1.0" }); + + defmt::write!(f, "{} {}\n", self.code, self.reason.unwrap_or("")); + + for (name, value) in self.headers.iter() { + if name.is_empty() { + break; + } + + defmt::write!(f, "{}: {}\n", name, value); + } + } +} + /// Websocket utilities pub mod ws { use crate::Method; @@ -935,6 +1011,17 @@ pub mod ws { } } + #[cfg(feature = "defmt")] + impl defmt::Format for UpgradeError { + fn format(&self, f: defmt::Formatter<'_>) { + match self { + Self::NoVersion => defmt::write!(f, "No Sec-WebSocket-Version header"), + Self::NoSecKey => defmt::write!(f, "No Sec-WebSocket-Key header"), + Self::UnsupportedVersion => defmt::write!(f, "Unsupported Sec-WebSocket-Version"), + } + } + } + #[cfg(feature = "std")] impl std::error::Error for UpgradeError {} diff --git a/edge-mdns/src/io.rs b/edge-mdns/src/io.rs index c11f418..61eb561 100644 --- a/edge-mdns/src/io.rs +++ b/edge-mdns/src/io.rs @@ -360,7 +360,11 @@ where ); if let Err(err) = send.send(remote, data).await { - warn!("Failed to reply privately to {}: {:?}", remote, err); + warn!( + "Failed to reply privately to {}: {:?}", + remote, + debug2format!(err) + ); } } else { // Otherwise, re-broadcast the response diff --git a/edge-mdns/src/lib.rs b/edge-mdns/src/lib.rs index 7a4b488..fa13cd5 100644 --- a/edge-mdns/src/lib.rs +++ b/edge-mdns/src/lib.rs @@ -438,6 +438,7 @@ impl AsRef<[u8]> for Buf<'_> { /// Type of request for `MdnsHandler::handle`. #[derive(Debug, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MdnsRequest<'a> { /// No incoming mDNS request. Send a broadcast message None, @@ -454,6 +455,7 @@ pub enum MdnsRequest<'a> { /// Return type for `MdnsHandler::handle`. #[derive(Debug, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MdnsResponse<'a> { None, Reply { data: &'a [u8], delay: bool }, @@ -879,7 +881,11 @@ where } if question.qname().name_eq(&answer.owner()) { - debug!("Answering question [{}] with: [{}]", question, answer); + debug!( + "Answering question [{}] with: [{}]", + debug2format!(question), + debug2format!(answer) + ); ab.push(answer)?; @@ -904,7 +910,7 @@ where | RecordDataChain::Next(AllRecordData::Txt(_)) | RecordDataChain::This(Txt(_)) ) { - debug!("Additional answer: [{}]", answer); + debug!("Additional answer: [{}]", debug2format!(answer)); aa.push(answer)?; diff --git a/edge-raw/src/ip.rs b/edge-raw/src/ip.rs index 34ddd89..ab474d5 100644 --- a/edge-raw/src/ip.rs +++ b/edge-raw/src/ip.rs @@ -34,6 +34,7 @@ where /// Represents a parsed IP header #[derive(Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Ipv4PacketHeader { /// Version pub version: u8, diff --git a/edge-raw/src/udp.rs b/edge-raw/src/udp.rs index 21e48de..64a1d40 100644 --- a/edge-raw/src/udp.rs +++ b/edge-raw/src/udp.rs @@ -41,6 +41,7 @@ where /// Represents a parsed UDP header #[derive(Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct UdpPacketHeader { /// Source port pub src: u16,