From a14d2564b32ef43bf9a163c974ee6cbc545633a3 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 3 Aug 2025 23:30:30 +0100 Subject: [PATCH 1/4] Unify app constructors with generic new --- README.md | 6 ++-- examples/echo.rs | 7 ++-- examples/metadata_routing.rs | 4 +-- examples/ping_pong.rs | 2 +- src/app.rs | 68 +++++++++++++++++++++++++++--------- tests/lifecycle.rs | 18 ++++++---- tests/response.rs | 8 ++--- tests/routes.rs | 16 ++++++--- tests/wireframe_protocol.rs | 15 ++++---- 9 files changed, 97 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 4fbc6550..9cab60d4 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,8 @@ details. `WireframeApp` defaults to a simple `Envelope` containing a message ID and raw payload bytes. Applications can supply their own envelope type by calling -`WireframeApp::<_, _, MyEnv>::new_with_envelope()`. The custom type must -implement the `Packet` trait: +`WireframeApp::<_, _, MyEnv>::new()`. The custom type must implement the +`Packet` trait: ```rust use wireframe::app::{Packet, WireframeApp}; @@ -107,7 +107,7 @@ impl Packet for MyEnv { fn from_parts(id: u32, data: Vec) -> Self { Self { id, data } } } -let app = WireframeApp::<_, _, MyEnv>::new_with_envelope() +let app = WireframeApp::<_, _, MyEnv>::new() .unwrap() .route(1, std::sync::Arc::new(|env: &MyEnv| Box::pin(async move { /* ... */ }))) .unwrap(); diff --git a/examples/echo.rs b/examples/echo.rs index 91840076..1791492c 100644 --- a/examples/echo.rs +++ b/examples/echo.rs @@ -5,7 +5,10 @@ use std::io; -use wireframe::{app::WireframeApp, server::WireframeServer}; +use wireframe::{ + app::{Envelope, WireframeApp}, + server::WireframeServer, +}; #[tokio::main] async fn main() -> io::Result<()> { @@ -14,7 +17,7 @@ async fn main() -> io::Result<()> { .unwrap() .route( 1, - std::sync::Arc::new(|_| { + std::sync::Arc::new(|_: &Envelope| { Box::pin(async move { println!("echo request received"); // `WireframeApp` automatically echoes the envelope back. diff --git a/examples/metadata_routing.rs b/examples/metadata_routing.rs index 8fa7e5b1..45459ac5 100644 --- a/examples/metadata_routing.rs +++ b/examples/metadata_routing.rs @@ -67,7 +67,7 @@ async fn main() -> io::Result<()> { .serializer(HeaderSerializer) .route( 1, - Arc::new(|_env| { + Arc::new(|_env: &Envelope| { Box::pin(async move { println!("received ping message"); }) @@ -76,7 +76,7 @@ async fn main() -> io::Result<()> { .unwrap() .route( 2, - Arc::new(|_env| { + Arc::new(|_env: &Envelope| { Box::pin(async move { println!("received pong message"); }) diff --git a/examples/ping_pong.rs b/examples/ping_pong.rs index edde533e..ed39b217 100644 --- a/examples/ping_pong.rs +++ b/examples/ping_pong.rs @@ -130,7 +130,7 @@ impl Transform> for Logging { fn build_app() -> AppResult { WireframeApp::new()? .serializer(BincodeSerializer) - .route(PING_ID, Arc::new(|_| Box::pin(ping_handler())))? + .route(PING_ID, Arc::new(|_: &Envelope| Box::pin(ping_handler())))? .wrap(PongMiddleware)? .wrap(Logging) } diff --git a/src/app.rs b/src/app.rs index 140b8a84..d82fc9de 100644 --- a/src/app.rs +++ b/src/app.rs @@ -140,7 +140,9 @@ impl std::error::Error for SendError { } impl From for SendError { - fn from(e: io::Error) -> Self { SendError::Io(e) } + fn from(e: io::Error) -> Self { + SendError::Io(e) + } } /// Envelope-like type used to wrap incoming and outgoing messages. @@ -198,19 +200,29 @@ pub struct Envelope { impl Envelope { /// Create a new [`Envelope`] with the provided id and payload. #[must_use] - pub fn new(id: u32, msg: Vec) -> Self { Self { id, msg } } + pub fn new(id: u32, msg: Vec) -> Self { + Self { id, msg } + } /// Consume the envelope, returning its id and payload bytes. #[must_use] - pub fn into_parts(self) -> (u32, Vec) { (self.id, self.msg) } + pub fn into_parts(self) -> (u32, Vec) { + (self.id, self.msg) + } } impl Packet for Envelope { - fn id(&self) -> u32 { self.id } + fn id(&self) -> u32 { + self.id + } - fn into_parts(self) -> (u32, Vec) { (self.id, self.msg) } + fn into_parts(self) -> (u32, Vec) { + (self.id, self.msg) + } - fn from_parts(id: u32, msg: Vec) -> Self { Self { id, msg } } + fn from_parts(id: u32, msg: Vec) -> Self { + Self { id, msg } + } } /// Number of idle polls before terminating a connection. @@ -247,27 +259,51 @@ where } } -impl WireframeApp { +impl WireframeApp +where + E: Packet, +{ /// Construct a new empty application builder. /// /// # Errors /// - /// This function currently never returns an error but uses the - /// [`Result`] type for forward compatibility. - pub fn new() -> Result { Ok(Self::default()) } -} + /// This function currently never returns an error but uses [`Result`] for + /// forward compatibility. + /// + /// # Examples + /// + /// ``` + /// use wireframe::app::{Packet, WireframeApp}; + /// + /// #[derive(bincode::Encode, bincode::BorrowDecode)] + /// struct MyEnv { id: u32, data: Vec } + /// + /// impl Packet for MyEnv { + /// fn id(&self) -> u32 { self.id } + /// fn into_parts(self) -> (u32, Vec) { (self.id, self.data) } + /// fn from_parts(id: u32, data: Vec) -> Self { Self { id, data } } + /// } + /// + /// let app = WireframeApp::<_, _, MyEnv>::new() + /// .expect("failed to create app"); + /// ``` + pub fn new() -> Result { + Ok(Self::default()) + } -impl WireframeApp -where - E: Packet, -{ /// Construct a new application builder using a custom envelope type. /// + /// Deprecated: call [`WireframeApp::new`] with explicit envelope type + /// parameters. + /// /// # Errors /// /// This function currently never returns an error but uses [`Result`] for /// forward compatibility. - pub fn new_with_envelope() -> Result { Ok(Self::default()) } + #[deprecated(note = "use `WireframeApp::<_, _, E>::new()` instead")] + pub fn new_with_envelope() -> Result { + Self::new() + } } impl WireframeApp diff --git a/tests/lifecycle.rs b/tests/lifecycle.rs index a81068b0..29a166f9 100644 --- a/tests/lifecycle.rs +++ b/tests/lifecycle.rs @@ -49,7 +49,7 @@ where let setup_cb = call_counting_callback(setup, state); let teardown_cb = call_counting_callback(teardown, ()); - WireframeApp::<_, _, E>::new_with_envelope() + WireframeApp::<_, _, E>::new() .expect("failed to create app") .on_connection_setup(move || setup_cb(())) .expect("setup callback") @@ -74,7 +74,7 @@ async fn setup_without_teardown_runs() { let setup_count = Arc::new(AtomicUsize::new(0)); let cb = call_counting_callback(&setup_count, ()); - let app = WireframeApp::new() + let app = WireframeApp::<_, _, Envelope>::new() .expect("failed to create app") .on_connection_setup(move || cb(())) .expect("setup callback"); @@ -89,7 +89,7 @@ async fn teardown_without_setup_does_not_run() { let teardown_count = Arc::new(AtomicUsize::new(0)); let cb = call_counting_callback(&teardown_count, ()); - let app = WireframeApp::new() + let app = WireframeApp::<_, _, Envelope>::new() .expect("failed to create app") .on_connection_teardown(cb) .expect("teardown callback"); @@ -106,11 +106,17 @@ struct StateEnvelope { } impl wireframe::app::Packet for StateEnvelope { - fn id(&self) -> u32 { self.id } + fn id(&self) -> u32 { + self.id + } - fn into_parts(self) -> (u32, Vec) { (self.id, self.msg) } + fn into_parts(self) -> (u32, Vec) { + (self.id, self.msg) + } - fn from_parts(id: u32, msg: Vec) -> Self { Self { id, msg } } + fn from_parts(id: u32, msg: Vec) -> Self { + Self { id, msg } + } } #[tokio::test] diff --git a/tests/response.rs b/tests/response.rs index 20a5857c..60b9de04 100644 --- a/tests/response.rs +++ b/tests/response.rs @@ -6,7 +6,7 @@ use bytes::BytesMut; use rstest::rstest; use wireframe::{ - app::WireframeApp, + app::{Envelope, WireframeApp}, frame::{Endianness, FrameProcessor, LengthFormat, LengthPrefixedProcessor}, message::Message, serializer::BincodeSerializer, @@ -39,7 +39,7 @@ impl<'de> bincode::BorrowDecode<'de, ()> for FailingResp { /// 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() + let app = WireframeApp::<_, _, Envelope>::new() .expect("failed to create app") .frame_processor(LengthPrefixedProcessor::default()) .serializer(BincodeSerializer); @@ -131,7 +131,7 @@ fn custom_length_roundtrip( #[tokio::test] async fn send_response_propagates_write_error() { - let app = WireframeApp::new() + let app = WireframeApp::<_, _, Envelope>::new() .expect("app creation failed") .frame_processor(LengthPrefixedProcessor::default()); @@ -190,7 +190,7 @@ fn encode_fails_for_length_too_large(#[case] fmt: LengthFormat, #[case] len: usi /// 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().expect("failed to create app"); + let app = WireframeApp::<_, _, Envelope>::new().expect("failed to create app"); let err = app .send_response(&mut Vec::new(), &FailingResp) .await diff --git a/tests/routes.rs b/tests/routes.rs index dbaa1652..cb2e68c4 100644 --- a/tests/routes.rs +++ b/tests/routes.rs @@ -25,11 +25,17 @@ struct TestEnvelope { } impl wireframe::app::Packet for TestEnvelope { - fn id(&self) -> u32 { self.id } + fn id(&self) -> u32 { + self.id + } - fn into_parts(self) -> (u32, Vec) { (self.id, self.msg) } + fn into_parts(self) -> (u32, Vec) { + (self.id, self.msg) + } - fn from_parts(id: u32, msg: Vec) -> Self { Self { id, msg } } + fn from_parts(id: u32, msg: Vec) -> Self { + Self { id, msg } + } } #[derive(bincode::Encode, bincode::BorrowDecode, PartialEq, Debug)] @@ -40,7 +46,7 @@ struct Echo(u8); async fn handler_receives_message_and_echoes_response() { let called = Arc::new(AtomicUsize::new(0)); let called_clone = called.clone(); - let app = WireframeApp::<_, _, TestEnvelope>::new_with_envelope() + let app = WireframeApp::<_, _, TestEnvelope>::new() .expect("failed to create app") .frame_processor(LengthPrefixedProcessor::default()) .route( @@ -79,7 +85,7 @@ async fn handler_receives_message_and_echoes_response() { #[tokio::test] async fn multiple_frames_processed_in_sequence() { - let app = WireframeApp::<_, _, TestEnvelope>::new_with_envelope() + let app = WireframeApp::<_, _, TestEnvelope>::new() .expect("failed to create app") .frame_processor(LengthPrefixedProcessor::default()) .route( diff --git a/tests/wireframe_protocol.rs b/tests/wireframe_protocol.rs index 79f107dc..e8707f58 100644 --- a/tests/wireframe_protocol.rs +++ b/tests/wireframe_protocol.rs @@ -14,11 +14,8 @@ use futures::stream; use rstest::rstest; use tokio_util::sync::CancellationToken; use wireframe::{ - ConnectionContext, - WireframeProtocol, - app::WireframeApp, - connection::ConnectionActor, - push::PushQueues, + ConnectionContext, WireframeProtocol, + app::{Envelope, WireframeApp}, connection::ConnectionActor, push::PushQueues, }; struct TestProtocol { @@ -37,7 +34,9 @@ impl WireframeProtocol for TestProtocol { self.counter.fetch_add(1, Ordering::SeqCst); } - fn before_send(&self, frame: &mut Self::Frame, _ctx: &mut ConnectionContext) { frame.push(1); } + fn before_send(&self, frame: &mut Self::Frame, _ctx: &mut ConnectionContext) { + frame.push(1); + } fn on_command_end(&self, _ctx: &mut ConnectionContext) { self.counter.fetch_add(1, Ordering::SeqCst); @@ -51,7 +50,7 @@ async fn builder_produces_protocol_hooks() { let protocol = TestProtocol { counter: counter.clone(), }; - let app = WireframeApp::new() + let app = WireframeApp::<_, _, Envelope>::new() .expect("failed to create app") .with_protocol(protocol); let mut hooks = app.protocol_hooks(); @@ -75,7 +74,7 @@ async fn connection_actor_uses_protocol_from_builder() { let protocol = TestProtocol { counter: counter.clone(), }; - let app = WireframeApp::new() + let app = WireframeApp::<_, _, Envelope>::new() .expect("failed to create app") .with_protocol(protocol); From 74b2f40df732121325d4aa9c7bd841d6db6ddd69 Mon Sep 17 00:00:00 2001 From: Leynos Date: Mon, 4 Aug 2025 14:02:48 +0100 Subject: [PATCH 2/4] Compact packet helpers --- src/app.rs | 24 ++++++------------------ tests/lifecycle.rs | 12 +++--------- tests/wireframe_protocol.rs | 7 +++++-- 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/app.rs b/src/app.rs index d82fc9de..4d80f913 100644 --- a/src/app.rs +++ b/src/app.rs @@ -140,9 +140,7 @@ impl std::error::Error for SendError { } impl From for SendError { - fn from(e: io::Error) -> Self { - SendError::Io(e) - } + fn from(e: io::Error) -> Self { SendError::Io(e) } } /// Envelope-like type used to wrap incoming and outgoing messages. @@ -200,29 +198,19 @@ pub struct Envelope { impl Envelope { /// Create a new [`Envelope`] with the provided id and payload. #[must_use] - pub fn new(id: u32, msg: Vec) -> Self { - Self { id, msg } - } + pub fn new(id: u32, msg: Vec) -> Self { Self { id, msg } } /// Consume the envelope, returning its id and payload bytes. #[must_use] - pub fn into_parts(self) -> (u32, Vec) { - (self.id, self.msg) - } + pub fn into_parts(self) -> (u32, Vec) { (self.id, self.msg) } } impl Packet for Envelope { - fn id(&self) -> u32 { - self.id - } + fn id(&self) -> u32 { self.id } - fn into_parts(self) -> (u32, Vec) { - (self.id, self.msg) - } + fn into_parts(self) -> (u32, Vec) { (self.id, self.msg) } - fn from_parts(id: u32, msg: Vec) -> Self { - Self { id, msg } - } + fn from_parts(id: u32, msg: Vec) -> Self { Self { id, msg } } } /// Number of idle polls before terminating a connection. diff --git a/tests/lifecycle.rs b/tests/lifecycle.rs index 29a166f9..297f3e5f 100644 --- a/tests/lifecycle.rs +++ b/tests/lifecycle.rs @@ -106,17 +106,11 @@ struct StateEnvelope { } impl wireframe::app::Packet for StateEnvelope { - fn id(&self) -> u32 { - self.id - } + fn id(&self) -> u32 { self.id } - fn into_parts(self) -> (u32, Vec) { - (self.id, self.msg) - } + fn into_parts(self) -> (u32, Vec) { (self.id, self.msg) } - fn from_parts(id: u32, msg: Vec) -> Self { - Self { id, msg } - } + fn from_parts(id: u32, msg: Vec) -> Self { Self { id, msg } } } #[tokio::test] diff --git a/tests/wireframe_protocol.rs b/tests/wireframe_protocol.rs index e8707f58..48445686 100644 --- a/tests/wireframe_protocol.rs +++ b/tests/wireframe_protocol.rs @@ -14,8 +14,11 @@ use futures::stream; use rstest::rstest; use tokio_util::sync::CancellationToken; use wireframe::{ - ConnectionContext, WireframeProtocol, - app::{Envelope, WireframeApp}, connection::ConnectionActor, push::PushQueues, + app::{Envelope, WireframeApp}, + connection::ConnectionActor, + push::PushQueues, + ConnectionContext, + WireframeProtocol, }; struct TestProtocol { From 8ad21d802ce4b54805484be44181ad6d29535a56 Mon Sep 17 00:00:00 2001 From: Leynos Date: Mon, 4 Aug 2025 15:01:53 +0100 Subject: [PATCH 3/4] Sort protocol test imports --- tests/wireframe_protocol.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/wireframe_protocol.rs b/tests/wireframe_protocol.rs index 48445686..69ce5022 100644 --- a/tests/wireframe_protocol.rs +++ b/tests/wireframe_protocol.rs @@ -14,11 +14,10 @@ use futures::stream; use rstest::rstest; use tokio_util::sync::CancellationToken; use wireframe::{ + ConnectionContext, WireframeProtocol, app::{Envelope, WireframeApp}, connection::ConnectionActor, push::PushQueues, - ConnectionContext, - WireframeProtocol, }; struct TestProtocol { From f7b5fe1724875edcd8bbaf71fe28d72a5b05487b Mon Sep 17 00:00:00 2001 From: Leynos Date: Mon, 4 Aug 2025 16:53:59 +0100 Subject: [PATCH 4/4] Format app and tests --- src/app.rs | 16 +++++++--------- tests/routes.rs | 12 +++--------- tests/wireframe_protocol.rs | 7 +++---- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/app.rs b/src/app.rs index 4d80f913..a84df128 100644 --- a/src/app.rs +++ b/src/app.rs @@ -264,7 +264,10 @@ where /// use wireframe::app::{Packet, WireframeApp}; /// /// #[derive(bincode::Encode, bincode::BorrowDecode)] - /// struct MyEnv { id: u32, data: Vec } + /// struct MyEnv { + /// id: u32, + /// data: Vec, + /// } /// /// impl Packet for MyEnv { /// fn id(&self) -> u32 { self.id } @@ -272,12 +275,9 @@ where /// fn from_parts(id: u32, data: Vec) -> Self { Self { id, data } } /// } /// - /// let app = WireframeApp::<_, _, MyEnv>::new() - /// .expect("failed to create app"); + /// let app = WireframeApp::<_, _, MyEnv>::new().expect("failed to create app"); /// ``` - pub fn new() -> Result { - Ok(Self::default()) - } + pub fn new() -> Result { Ok(Self::default()) } /// Construct a new application builder using a custom envelope type. /// @@ -289,9 +289,7 @@ where /// This function currently never returns an error but uses [`Result`] for /// forward compatibility. #[deprecated(note = "use `WireframeApp::<_, _, E>::new()` instead")] - pub fn new_with_envelope() -> Result { - Self::new() - } + pub fn new_with_envelope() -> Result { Self::new() } } impl WireframeApp diff --git a/tests/routes.rs b/tests/routes.rs index cb2e68c4..93888cab 100644 --- a/tests/routes.rs +++ b/tests/routes.rs @@ -25,17 +25,11 @@ struct TestEnvelope { } impl wireframe::app::Packet for TestEnvelope { - fn id(&self) -> u32 { - self.id - } + fn id(&self) -> u32 { self.id } - fn into_parts(self) -> (u32, Vec) { - (self.id, self.msg) - } + fn into_parts(self) -> (u32, Vec) { (self.id, self.msg) } - fn from_parts(id: u32, msg: Vec) -> Self { - Self { id, msg } - } + fn from_parts(id: u32, msg: Vec) -> Self { Self { id, msg } } } #[derive(bincode::Encode, bincode::BorrowDecode, PartialEq, Debug)] diff --git a/tests/wireframe_protocol.rs b/tests/wireframe_protocol.rs index 69ce5022..774cb79e 100644 --- a/tests/wireframe_protocol.rs +++ b/tests/wireframe_protocol.rs @@ -14,7 +14,8 @@ use futures::stream; use rstest::rstest; use tokio_util::sync::CancellationToken; use wireframe::{ - ConnectionContext, WireframeProtocol, + ConnectionContext, + WireframeProtocol, app::{Envelope, WireframeApp}, connection::ConnectionActor, push::PushQueues, @@ -36,9 +37,7 @@ impl WireframeProtocol for TestProtocol { self.counter.fetch_add(1, Ordering::SeqCst); } - fn before_send(&self, frame: &mut Self::Frame, _ctx: &mut ConnectionContext) { - frame.push(1); - } + fn before_send(&self, frame: &mut Self::Frame, _ctx: &mut ConnectionContext) { frame.push(1); } fn on_command_end(&self, _ctx: &mut ConnectionContext) { self.counter.fetch_add(1, Ordering::SeqCst);