diff --git a/README.md b/README.md index 6cd12d5..7a7535a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,101 @@ -# session-rs -A lightweight async protocol for WebSocket. +
+
+
A lightweight, async WebSocket protocol for Rust.
+ +## Introduction + +This library provides **type-safe WebSocket communication** with a request-response and notification system built on top of a flexible protocol. +It ensures compile-time guarantees for message structure, reduces runtime errors, and simplifies building Rust client/server applications. + +- **Dynamic Methods**: Each message includes a method enum for type safety. +- **Typed Requests & Responses**: Automatic serialization and deserialization. +- **Optional Notifications**: Send asynchronous notifications across sessions. + +## Features + +- Fully typed WebSocket sessions +- Type-safe request/response mechanism +- Optional typed notifications (Todo) +- Lightweight, minimal runtime overhead +- Async-first with Tokio support + +## Installation + +```bash +cargo add session-rs +``` + +--- + +### **Basic Example (client)** + +```rust +#[derive(Debug, Serialize, Deserialize)] +struct Data; + +impl Method for Data { + const NAME: &'static str = "data"; + type Request = String; + type Response = String; + type Error = String; +} + +let session = Session::connect("127.0.0.1:8080", "/").await?; + +session.start_receiver(); + +session + .request::("Hello from client".to_string()) + .await?; +``` + +### **Basic Example (server)** + +```rust +#[derive(Debug, Serialize, Deserialize)] +struct Data; + +impl Method for Data { + const NAME: &'static str = "data"; + type Request = String; + type Response = String; + type Error = String; +} + +let server = SessionServer::bind("127.0.0.1:8080").await?; + +server + .session_loop(async |session, addr| { + // This will run on every new client + + Ok(()) + }).await; +``` + +## Protocol + +#### Request + +The request `id` is separated from the peer, and will increment only on it's requests. + +```json +{ "type": "request", "id": 1, "method": "data", "data": "Hello from client" } +``` + +#### Response + +The response `id` **must** remain the same as the request. + +```json +{ "type": "response", "id": 1, "result": "Hello from server" } +``` + +#### Notifications + +A notification is a method that doesn't need validation or output, it simply notifies a peer for a specific information + +```json +{ "type": "notification", "result": "Hello from server" } +``` diff --git a/examples/server.rs b/examples/server.rs index 0d6fb8e..77583b1 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,7 +1,5 @@ use serde::{Deserialize, Serialize}; -use tokio::net::TcpListener; - -use session_rs::{Method, session::Session, ws::WebSocket}; +use session_rs::{Method, server::SessionServer}; #[derive(Debug, Serialize, Deserialize)] struct Data; @@ -15,23 +13,10 @@ impl Method for Data { #[tokio::main(flavor = "current_thread")] async fn main() -> session_rs::Result<()> { - let listener = TcpListener::bind("127.0.0.1:8080").await?; - println!("Server listening on ws://127.0.0.1:8080"); - - loop { - let (stream, addr) = listener.accept().await?; - println!("New connection: {}", addr); - - tokio::spawn(async move { - // Wrap session in Arc so tasks can share it - let session = Session::from_ws( - WebSocket::handshake(stream) - .await - .expect("Failed to initialize websocket"), - ); - - session.start_receiver(); + let server = SessionServer::bind("127.0.0.1:8080").await?; + server + .session_loop(async |session, _| { session .on::(async |_, req| { println!("Msg from client: {req}"); @@ -43,6 +28,8 @@ async fn main() -> session_rs::Result<()> { Ok("Hello from server".to_string()) }) .await; - }); - } + + Ok(()) + }) + .await } diff --git a/logo.svg b/logo.svg new file mode 100644 index 0000000..7f42bb9 --- /dev/null +++ b/logo.svg @@ -0,0 +1,28 @@ + diff --git a/src/server.rs b/src/server.rs index 7754b94..4b4db24 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1 +1,41 @@ -pub struct SessionServer(); +use std::{net::SocketAddr, sync::Arc}; + +use tokio::net::TcpListener; + +use crate::{session::Session, ws::WebSocket}; + +pub struct SessionServer { + listener: TcpListener, +} + +impl SessionServer { + pub async fn bind(addr: &str) -> crate::Result