From 8d646ca7b6cb98592640a7d894c68ad5270ec5bd Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 14:47:52 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`cod?= =?UTF-8?q?ex/define-endianness-enum-and-refactor-lengthprefixedprocessor`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @leynos. * https://github.com/leynos/wireframe/pull/87#issuecomment-2991876730 The following files were modified: * `src/app.rs` * `src/frame.rs` * `tests/response.rs` --- src/app.rs | 4 +++ src/frame.rs | 73 +++++++++++++++++++++++++++++++++++++++++------ tests/response.rs | 25 ++++++++++++++++ 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/src/app.rs b/src/app.rs index 1d6a308a..a9749cc0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -147,6 +147,10 @@ where S: Serializer + Default, C: Send + 'static, { + /// Creates a new `WireframeApp` instance with default configuration. + /// + /// Initialises empty routes, services, middleware, and application data. Sets the + /// default frame processor and serializer, with no connection lifecycle hooks. fn default() -> Self { Self { routes: HashMap::new(), diff --git a/src/frame.rs b/src/frame.rs index aaa0dfab..0c7e7f74 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -27,24 +27,46 @@ pub struct LengthFormat { impl LengthFormat { /// Create a new [`LengthFormat`]. #[must_use] - pub const fn new(bytes: usize, endianness: Endianness) -> Self { Self { bytes, endianness } } + /// Creates a new `LengthFormat` with the specified number of bytes and endianness for the length prefix. +/// +/// # Parameters +/// - `bytes`: The number of bytes used for the length prefix. +/// - `endianness`: The byte order for encoding and decoding the length prefix. +/// +/// # Returns +/// A `LengthFormat` configured with the given size and endianness. +pub const fn new(bytes: usize, endianness: Endianness) -> Self { Self { bytes, endianness } } /// Two byte big-endian prefix. #[must_use] - pub const fn u16_be() -> Self { Self::new(2, Endianness::Big) } + /// Creates a `LengthFormat` for a 2-byte big-endian length prefix. +pub const fn u16_be() -> Self { Self::new(2, Endianness::Big) } /// Two byte little-endian prefix. #[must_use] - pub const fn u16_le() -> Self { Self::new(2, Endianness::Little) } + /// Creates a `LengthFormat` for a 2-byte little-endian length prefix. +pub const fn u16_le() -> Self { Self::new(2, Endianness::Little) } /// Four byte big-endian prefix. #[must_use] - pub const fn u32_be() -> Self { Self::new(4, Endianness::Big) } + /// Creates a `LengthFormat` for a 4-byte big-endian length prefix. +pub const fn u32_be() -> Self { Self::new(4, Endianness::Big) } /// Four byte little-endian prefix. #[must_use] - pub const fn u32_le() -> Self { Self::new(4, Endianness::Little) } + /// Creates a `LengthFormat` for a 4-byte little-endian length prefix. +pub const fn u32_le() -> Self { Self::new(4, Endianness::Little) } + /// Reads a length prefix from a byte slice according to the configured prefix size and endianness. + /// + /// # Parameters + /// - `bytes`: The byte slice containing the length prefix. Must be at least as long as the configured prefix size. + /// + /// # Returns + /// The decoded length as a `usize` if successful. + /// + /// # Errors + /// Returns an error if the prefix size is unsupported or if the decoded length does not fit in a `usize`. fn read_len(&self, bytes: &[u8]) -> io::Result { let len = match (self.bytes, self.endianness) { (1, _) => u64::from(u8::from_ne_bytes([bytes[0]])), @@ -72,6 +94,16 @@ impl LengthFormat { usize::try_from(len).map_err(|_| io::Error::other("frame too large")) } + /// Writes a length prefix to the destination buffer using the configured size and endianness. + /// + /// Returns an error if the length is too large to fit in the configured prefix size or if the prefix size is unsupported. + /// + /// # Parameters + /// - `len`: The length value to encode and write. + /// - `dst`: The buffer to which the encoded length prefix will be appended. + /// + /// # Errors + /// Returns an error if `len` exceeds the maximum value for the configured prefix size or if the prefix size is not supported. fn write_len(&self, len: usize, dst: &mut BytesMut) -> io::Result<()> { match (self.bytes, self.endianness) { (1, _) => dst.put_u8( @@ -120,7 +152,10 @@ impl LengthFormat { } impl Default for LengthFormat { - fn default() -> Self { Self::u32_be() } + /// Returns a `LengthFormat` using a 4-byte big-endian length prefix. +/// +/// This is the default format for length-prefixed framing. +fn default() -> Self { Self::u32_be() } } /// Trait defining how raw bytes are decoded into frames and how frames are @@ -161,17 +196,35 @@ pub struct LengthPrefixedProcessor { impl LengthPrefixedProcessor { /// Construct a processor with the provided [`LengthFormat`]. #[must_use] - pub const fn new(format: LengthFormat) -> Self { Self { format } } + /// Creates a new `LengthPrefixedProcessor` with the specified length prefix format. +/// +/// # Parameters +/// - `format`: The length prefix format to use for framing. +/// +/// # Returns +/// A `LengthPrefixedProcessor` configured with the given length format. +pub const fn new(format: LengthFormat) -> Self { Self { format } } } impl Default for LengthPrefixedProcessor { - fn default() -> Self { Self::new(LengthFormat::default()) } + /// Creates a `LengthPrefixedProcessor` using the default length format (4-byte big-endian prefix). +/// +/// # Returns +/// A processor configured for 4-byte big-endian length-prefixed framing. +fn default() -> Self { Self::new(LengthFormat::default()) } } impl FrameProcessor for LengthPrefixedProcessor { type Frame = Vec; type Error = std::io::Error; + /// Attempts to decode a single length-prefixed frame from the source buffer. + /// + /// Returns `Ok(Some(frame))` if a complete frame is available, `Ok(None)` if + /// more data is needed, or an error if the length prefix is invalid or cannot + /// be read according to the configured format. + /// + /// The source buffer is advanced past the decoded frame and its length prefix. fn decode(&self, src: &mut BytesMut) -> Result, Self::Error> { if src.len() < self.format.bytes { return Ok(None); @@ -184,6 +237,10 @@ impl FrameProcessor for LengthPrefixedProcessor { Ok(Some(src.split_to(len).to_vec())) } + /// Encodes a frame by prefixing it with its length and appending it to the destination buffer. + /// + /// The length prefix format is determined by the processor's configuration. Returns an error + /// if the frame length cannot be represented in the configured format. fn encode(&self, frame: &Self::Frame, dst: &mut BytesMut) -> Result<(), Self::Error> { dst.reserve(self.format.bytes + frame.len()); self.format.write_len(frame.len(), dst)?; diff --git a/tests/response.rs b/tests/response.rs index b90f6988..7b024e81 100644 --- a/tests/response.rs +++ b/tests/response.rs @@ -35,6 +35,8 @@ impl<'de> bincode::BorrowDecode<'de, ()> for FailingResp { } #[tokio::test] +/// Tests that sending a response serialises and frames the data correctly, +/// and that the response can be decoded and deserialised back to its original value asynchronously. async fn send_response_encodes_and_frames() { let app = WireframeApp::new() .unwrap() @@ -52,6 +54,9 @@ async fn send_response_encodes_and_frames() { } #[tokio::test] +/// Tests that decoding with an incomplete length prefix header returns `None` and does not consume any bytes from the buffer. +/// +/// This ensures that the decoder waits for the full header before attempting to decode a frame. async fn length_prefixed_decode_requires_complete_header() { let processor = LengthPrefixedProcessor::default(); let mut buf = BytesMut::from(&[0x00, 0x00, 0x00][..]); // only 3 bytes @@ -60,6 +65,10 @@ async fn length_prefixed_decode_requires_complete_header() { } #[tokio::test] +/// Tests that decoding with a complete length prefix but incomplete frame data returns `None` +/// and retains all bytes in the buffer. +/// +/// Ensures that the decoder does not consume any bytes when the full frame is not yet available. async fn length_prefixed_decode_requires_full_frame() { let processor = LengthPrefixedProcessor::default(); let mut buf = BytesMut::from(&[0x00, 0x00, 0x00, 0x05, 0x01, 0x02][..]); @@ -95,6 +104,10 @@ impl tokio::io::AsyncWrite for FailingWriter { } #[tokio::test] +/// Tests that `send_response` correctly propagates I/O errors encountered during writing. +/// +/// This test uses a writer that always fails on write operations and asserts that +/// the resulting error is of the `Io` variant. async fn send_response_propagates_write_error() { let app = WireframeApp::new() .unwrap() @@ -109,6 +122,10 @@ async fn send_response_propagates_write_error() { } #[tokio::test] +/// Tests that `send_response` returns a serialization error when encoding fails. +/// +/// This test sends a `FailingResp` using `send_response` and asserts that the resulting +/// error is of the `Serialize` variant, indicating a failure during response encoding. async fn send_response_returns_encode_error() { let app = WireframeApp::new().unwrap(); let err = app @@ -119,6 +136,10 @@ async fn send_response_returns_encode_error() { } #[test] +/// Tests roundtrip encoding and decoding of a frame using a two-byte big-endian length prefix. +/// +/// Verifies that a frame encoded with a `LengthPrefixedProcessor` configured for a 2-byte +/// big-endian length format can be correctly decoded back to its original contents. fn custom_two_byte_big_endian_roundtrip() { let fmt = LengthFormat::u16_be(); let processor = LengthPrefixedProcessor::new(fmt); @@ -130,6 +151,10 @@ fn custom_two_byte_big_endian_roundtrip() { } #[test] +/// Tests roundtrip encoding and decoding of a frame using a four-byte little-endian length prefix. +/// +/// Verifies that the encoded buffer contains the correct little-endian length prefix and that +/// decoding restores the original frame. fn custom_four_byte_little_endian_roundtrip() { let fmt = LengthFormat::u32_le(); let processor = LengthPrefixedProcessor::new(fmt); From 3c2f732f3a363e2a0db02bb194b6671e500eb477 Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 20 Jun 2025 16:01:46 +0100 Subject: [PATCH 2/2] Move attributes below doc comments (#90) --- src/frame.rs | 90 +++++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/frame.rs b/src/frame.rs index 0c7e7f74..97e31dc5 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -25,48 +25,47 @@ pub struct LengthFormat { } impl LengthFormat { - /// Create a new [`LengthFormat`]. + /// Creates a new `LengthFormat` with the specified number of bytes and + /// endianness for the length prefix. + /// + /// # Parameters + /// - `bytes`: The number of bytes used for the length prefix. + /// - `endianness`: The byte order for encoding and decoding the length prefix. + /// + /// # Returns + /// A `LengthFormat` configured with the given size and endianness. #[must_use] - /// Creates a new `LengthFormat` with the specified number of bytes and endianness for the length prefix. -/// -/// # Parameters -/// - `bytes`: The number of bytes used for the length prefix. -/// - `endianness`: The byte order for encoding and decoding the length prefix. -/// -/// # Returns -/// A `LengthFormat` configured with the given size and endianness. -pub const fn new(bytes: usize, endianness: Endianness) -> Self { Self { bytes, endianness } } + pub const fn new(bytes: usize, endianness: Endianness) -> Self { Self { bytes, endianness } } - /// Two byte big-endian prefix. - #[must_use] /// Creates a `LengthFormat` for a 2-byte big-endian length prefix. -pub const fn u16_be() -> Self { Self::new(2, Endianness::Big) } - - /// Two byte little-endian prefix. #[must_use] - /// Creates a `LengthFormat` for a 2-byte little-endian length prefix. -pub const fn u16_le() -> Self { Self::new(2, Endianness::Little) } + pub const fn u16_be() -> Self { Self::new(2, Endianness::Big) } - /// Four byte big-endian prefix. + /// Creates a `LengthFormat` for a 2-byte little-endian length prefix. #[must_use] - /// Creates a `LengthFormat` for a 4-byte big-endian length prefix. -pub const fn u32_be() -> Self { Self::new(4, Endianness::Big) } + pub const fn u16_le() -> Self { Self::new(2, Endianness::Little) } - /// Four byte little-endian prefix. + /// Creates a `LengthFormat` for a 4-byte big-endian length prefix. #[must_use] + pub const fn u32_be() -> Self { Self::new(4, Endianness::Big) } + /// Creates a `LengthFormat` for a 4-byte little-endian length prefix. -pub const fn u32_le() -> Self { Self::new(4, Endianness::Little) } + #[must_use] + pub const fn u32_le() -> Self { Self::new(4, Endianness::Little) } - /// Reads a length prefix from a byte slice according to the configured prefix size and endianness. + /// Reads a length prefix from a byte slice according to the configured prefix size and + /// endianness. /// /// # Parameters - /// - `bytes`: The byte slice containing the length prefix. Must be at least as long as the configured prefix size. + /// - `bytes`: The byte slice containing the length prefix. Must be at least as long as the + /// configured prefix size. /// /// # Returns /// The decoded length as a `usize` if successful. /// /// # Errors - /// Returns an error if the prefix size is unsupported or if the decoded length does not fit in a `usize`. + /// Returns an error if the prefix size is unsupported or if the decoded length does not fit in + /// a `usize`. fn read_len(&self, bytes: &[u8]) -> io::Result { let len = match (self.bytes, self.endianness) { (1, _) => u64::from(u8::from_ne_bytes([bytes[0]])), @@ -96,14 +95,16 @@ pub const fn u32_le() -> Self { Self::new(4, Endianness::Little) } /// Writes a length prefix to the destination buffer using the configured size and endianness. /// - /// Returns an error if the length is too large to fit in the configured prefix size or if the prefix size is unsupported. + /// Returns an error if the length is too large to fit in the configured prefix size or if the + /// prefix size is unsupported. /// /// # Parameters /// - `len`: The length value to encode and write. /// - `dst`: The buffer to which the encoded length prefix will be appended. /// /// # Errors - /// Returns an error if `len` exceeds the maximum value for the configured prefix size or if the prefix size is not supported. + /// Returns an error if `len` exceeds the maximum value for the configured prefix size or if the + /// prefix size is not supported. fn write_len(&self, len: usize, dst: &mut BytesMut) -> io::Result<()> { match (self.bytes, self.endianness) { (1, _) => dst.put_u8( @@ -153,9 +154,9 @@ pub const fn u32_le() -> Self { Self::new(4, Endianness::Little) } impl Default for LengthFormat { /// Returns a `LengthFormat` using a 4-byte big-endian length prefix. -/// -/// This is the default format for length-prefixed framing. -fn default() -> Self { Self::u32_be() } + /// + /// This is the default format for length-prefixed framing. + fn default() -> Self { Self::u32_be() } } /// Trait defining how raw bytes are decoded into frames and how frames are @@ -194,24 +195,25 @@ pub struct LengthPrefixedProcessor { } impl LengthPrefixedProcessor { - /// Construct a processor with the provided [`LengthFormat`]. + /// Creates a new `LengthPrefixedProcessor` with the specified length prefix + /// format. + /// + /// # Parameters + /// - `format`: The length prefix format to use for framing. + /// + /// # Returns + /// A `LengthPrefixedProcessor` configured with the given length format. #[must_use] - /// Creates a new `LengthPrefixedProcessor` with the specified length prefix format. -/// -/// # Parameters -/// - `format`: The length prefix format to use for framing. -/// -/// # Returns -/// A `LengthPrefixedProcessor` configured with the given length format. -pub const fn new(format: LengthFormat) -> Self { Self { format } } + pub const fn new(format: LengthFormat) -> Self { Self { format } } } impl Default for LengthPrefixedProcessor { - /// Creates a `LengthPrefixedProcessor` using the default length format (4-byte big-endian prefix). -/// -/// # Returns -/// A processor configured for 4-byte big-endian length-prefixed framing. -fn default() -> Self { Self::new(LengthFormat::default()) } + /// Creates a `LengthPrefixedProcessor` using the default length format (4-byte big-endian + /// prefix). + /// + /// # Returns + /// A processor configured for 4-byte big-endian length-prefixed framing. + fn default() -> Self { Self::new(LengthFormat::default()) } } impl FrameProcessor for LengthPrefixedProcessor {