rustwave 0.1.0

A lightweight async event-driven WebSocket framework for Rust
Documentation
# RustWave

A lightweight async event-driven WebSocket framework for Rust.

## Installation

```toml
[dependencies]
rustwave = "0.1"
tokio = { version = "1", features = ["full"] }
```

## Table of Contents

- [Basic Server]#basic-server
- [Basic Client]#basic-client
- [Namespaces]#namespaces
- [Rooms]#rooms
- [Broadcasting]#broadcasting
- [Emit to a Specific Client]#emit-to-a-specific-client
- [Connection / Disconnection Events]#connection--disconnection-events
- [Typed Payloads]#typed-payloads
- [Checking Client Status]#checking-client-status
- [Force Disconnect]#force-disconnect

---

## Basic Server

```rust
use rustwave::{Server, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let server = Server::bind("127.0.0.1:8080");

    server.on("message", |data, ctx| async move {
        println!("Client {} say: {}", ctx.get_socket_id(), data);
        let _ = ctx.emit("message", "got it!").await;
    });

    server.run().await?;
    Ok(())
}
```

## Basic Client

```rust
use rustwave::{Client, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let client = Client::connect("ws://127.0.0.1:8080").await?;

    client.on("message", |data| async move {
        println!("Server say: {}", data);
    });

    client.emit("message", "hello").await?;
    client.wait().await;

    Ok(())
}
```

## Namespaces

```rust
use rustwave::{Server, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let server = Server::bind("127.0.0.1:8080");

    // events on the root namespace ("/")
    server.on("message", |data, ctx| async move {
        println!("[root] {}: {}", ctx.get_socket_id(), data);
    });

    // custom namespace
    let chat = server.namespace("/chat");
    chat.on("message", |data, ctx| async move {
        println!("[chat] {}: {}", ctx.get_socket_id(), data);
    });

    server.run().await?;
    Ok(())
}
```

Connect a client to a specific namespace by including it in the URL:

```rust
let root_client = Client::connect("ws://127.0.0.1:8080/").await?;
let chat_client = Client::connect("ws://127.0.0.1:8080/chat").await?;
```

## Rooms

```rust
server.on("connection", |_data, ctx| async move {
    ctx.join_room("room-1");
});

server.on("leave", |_data, ctx| async move {
    ctx.leave_room("room-1");
});

server.on("message", |data, ctx| async move {
    let text = data.parse::<String>().unwrap_or_default();
    let _ = ctx.broadcast_to_room("room-1", "room-message", text).await;
});
```

## Broadcasting

```rust
// send to everyone in the namespace
server.on("message", |data, ctx| async move {
    let _ = ctx.broadcast("message", data.raw()).send().await;
});

// send to everyone in a room
server.on("message", |data, ctx| async move {
    let _ = ctx.broadcast("message", data.raw())
        .room("room-1")
        .send()
        .await;
});

// send to everyone in a room, except the sender
server.on("message", |data, ctx| async move {
    let sender_id = ctx.get_socket_id();
    let _ = ctx.broadcast("message", data.raw())
        .room("room-1")
        .expect(sender_id)
        .send()
        .await;
});

// shorthand: broadcast to room, excluding sender
server.on("message", |data, ctx| async move {
    let sender_id = ctx.get_socket_id();
    let _ = ctx.broadcast_to_expect("room-1", sender_id, "message", data.raw()).await;
});

// broadcast from a Namespace handle directly (e.g. outside a handler)
let chat = server.namespace("/chat");
chat.emit("announcement", "server restarting soon").await?;
chat.emit_to_room("room-1", "message", "hello room!").await?;
```

## Emit to a Specific Client

```rust
use uuid::Uuid;

server.on("connection", |_data, ctx| async move {
    let _ = ctx.emit("welcome", "hi there!").await;
});

// or from a Namespace handle if you already have the socket_id
let chat = server.namespace("/chat");
let socket_id: Uuid = /* ... */;
chat.emit_to(&socket_id, "private-message", "Hello there").await?;
```

## Connection / Disconnection Events

```rust
server.on("connection", |_data, ctx| async move {
    println!("client connected: {}", ctx.get_socket_id());
});

server.on("disconnect", |_data, ctx| async move {
    println!("client disconnected: {}", ctx.get_socket_id());
});
```

Client-side disconnect handling:

```rust
client.on("disconnect", |_data| async move {
    println!("disconnected from server");
});

client.close().await; // close the connection manually
```

## Typed Payloads

```rust
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct Message {
    text: String,
    room: String,
}

server.on("message", |data, ctx| async move {
    if let Ok(msg) = data.parse::<Message>() {
        println!("Client {} say: {} in {}", ctx.get_socket_id(), msg.text, msg.room);
    }
});

// sending a typed struct works too
client.emit("message", Message {
    text: "hey!".to_string(),
    room: "general".to_string(),
}).await?;
```

## Checking Client Status

```rust
let chat = server.namespace("/chat");

println!("connected clients: {}", chat.get_client_count());

if chat.is_connected(&some_socket_id) {
    println!("still online");
}
```

## Force Disconnect

```rust
let chat = server.namespace("/chat");

// disconnect a single client
chat.disconnect(&some_socket_id).await?;

// disconnect everyone in the namespace
chat.disconnect_all().await?;
```

---

## Example Chat Application

A complete chat application demonstrating:

- Client & Server
- Rooms
- Broadcasting
- Typed payloads
- Join room events

| Example | Link                                                      |
| ------- | --------------------------------------------------------- |
| Server  | [`examples/terminal_server.rs`]examples/chat_server.rs |
| Client  | [`examples/terminal_client.rs`]examples/chat_client.rs |

Run the server:

```bash
cargo run --example terminal_server
```

Run one or more clients:

```bash
cargo run --example terminal_client
```