sioc 0.2.0

Async Socket.IO client with type-safe event handling
Documentation
# sioc

Typed, ergonomic [Socket.IO protocol v5](https://socket.io/docs/v4/socket-io-protocol/) client for Rust.

## What is Socket.IO?

The [Socket.IO protocol v5](https://socket.io/docs/v4/socket-io-protocol/) is a real-time, bidirectional event-based communication protocol built on top of the [Engine.IO protocol v4](https://socket.io/docs/v4/engine-io-protocol/).

It adds features such as _namespaces_, which allow multiple channels over a single connection; _events_, which are named messages with structured data; _acknowledgements_, enabling request/response patterns over events; and support for _binary attachments_.

## Features

- Supports Socket.IO protocol v5 and Engine.IO protocol v4.
- Async networking built on [Tokio]https://tokio.rs, [reqwest]https://docs.rs/reqwest, and [tokio-tungstenite]https://docs.rs/tokio-tungstenite.
- No boxed futures and a lock-free actor model. Channel-based event loops avoid callback borrow issues and fit stateful applications naturally.
- Derive macros for events, acks, serialization, and routing.
- Zero-copy packet parsing via [`bytestring`]https://docs.rs/bytestring and [`bytes`]https://docs.rs/bytes.

## Quick start

This example requires a running Socket.IO server. See the `quick-start` example for a self-contained demo.

```rust,no_run
use sioc::prelude::*;
use std::time::Duration;
use url::Url;

#[derive(Debug, EventType, DeserializePayload)]
#[sioc(event(name = "greeting"))]
struct Greeting {
    message: String,
}

#[derive(Debug, EventType, SerializePayload)]
#[sioc(event(name = "reply"))]
struct Reply {
    text: String,
}

#[derive(Debug, EventType, DeserializePayload)]
#[sioc(event(name = "poll", ack = "Vote"))]
struct Survey {
    question: String,
    options: Vec<String>,
}

#[derive(Debug, AckType, SerializePayload)]
struct Vote(usize);

#[derive(Debug, EventType, SerializePayload)]
#[sioc(event(name = "join", ack = "RoomInfo"))]
struct Join {
    room: String,
}

#[derive(Debug, AckType, DeserializePayload)]
struct RoomInfo {
    count: u32,
}

#[derive(Debug, EventRouter)]
enum AppEvent {
    Greeting(Event<Greeting>),
    Survey(Event<Survey>),
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let url = Url::parse("http://localhost:3000")?;

    let client = ClientBuilder::new(url).open()?;
    let (tx, mut rx) = client.connect("/").await?;

    let Ack { payload: RoomInfo { count }, .. } = tx
        .emit(Join { room: "lobby".into() })
        .await?
        .timeout(Duration::from_secs(5))
        .await?;

    println!("joined lobby with {count} members");

    while let Some(event) = rx.listen::<AppEvent>().await? {
        match event {
            AppEvent::Greeting(Event { payload: Greeting { message }, .. }) => {
                println!("greeting: {message}");
                tx.emit(Reply { text: "glad to be here!".into() }).await?;
            }

            AppEvent::Survey(Event { payload: Survey { question, options }, id, .. }) => {
                println!("poll: {question} ({} options)", options.len());
                tx.acknowledge(id, Vote(0)).await?;
            }
        }
    }

    tx.disconnect().await?;

    client.join().await?;

    Ok(())
}
```

## License

Licensed under MIT OR Apache-2.0.