Skip to content

Commit

Permalink
doc(socketio): factor-out repetitive documentation (#394)
Browse files Browse the repository at this point in the history
  • Loading branch information
Totodore authored Nov 5, 2024
1 parent 36331fa commit 9a58244
Show file tree
Hide file tree
Showing 18 changed files with 562 additions and 1,260 deletions.
19 changes: 19 additions & 0 deletions crates/socketioxide/docs/operators/broadcast.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Broadcast to all sockets without any filtering (except the current socket).
If you want to include the current socket use emit operators from the [`io`] global context.

[`io`]: crate::SocketIo

# Example
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
fn handler(io: SocketIo, socket: SocketRef, Data(data): Data::<Value>) {
// This message will be broadcast to all sockets in this namespace except this one.
socket.broadcast().emit("test", &data);
// This message will be broadcast to all sockets in this namespace, including this one.
io.emit("test", &data);
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |s: SocketRef| s.on("test", handler));
```
12 changes: 12 additions & 0 deletions crates/socketioxide/docs/operators/disconnect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Disconnect all sockets selected with the previous operators.

# Example
```rust
# use socketioxide::{SocketIo, extract::*};
fn handler(socket: SocketRef) {
// Disconnect all sockets in the room1 and room3 rooms, except for room2.
socket.within("room1").within("room3").except("room2").disconnect().unwrap();
}
let (_, io) = SocketIo::new_svc();
io.ns("/", |s: SocketRef| s.on("test", handler));
```
94 changes: 94 additions & 0 deletions crates/socketioxide/docs/operators/emit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Emit a message to one or many clients

If you provide tuple-like data (tuples, arrays), it will be considered as multiple arguments.
Therefore, if you want to send an array as the _first_ argument of the payload,
you need to wrap it in an array or a tuple. A [`Vec`] will always be considered a single argument.

# Emitting binary data
To emit binary data, you must use a data type that implements [`Serialize`] as binary data.
Currently, if you use `Vec<u8>`, it will be considered a sequence of numbers rather than binary data.
To handle this, you can either use a special type like [`Bytes`] or the [`serde_bytes`] crate.
If you want to emit generic data that may contain binary, use [`rmpv::Value`] instead of
[`serde_json::Value`], as binary data will otherwise be serialized as a sequence of numbers.

# Errors
* When encoding the data, a [`SendError::Serialize`] may be returned.
* If the underlying engine.io connection is closed, a [`SendError::Socket(SocketError::Closed)`]
will be returned, and the data you attempted to send will be included in the error.
* If the packet buffer is full, a [`SendError::Socket(SocketError::InternalChannelFull)`]
will be returned, and the data you attempted to send will be included in the error.
See the [`SocketIoBuilder::max_buffer_size`] option for more information on internal buffer configuration.

[`SocketIoBuilder::max_buffer_size`]: crate::SocketIoBuilder#method.max_buffer_size
[`SendError::Serialize`]: crate::SendError::Serialize
[`SendError::Socket(SocketError::Closed)`]: crate::SocketError::Closed
[`SendError::Socket(SocketError::InternalChannelFull)`]: crate::SocketError::InternalChannelFull
[`Bytes`]: bytes::Bytes
[`serde_bytes`]: https://docs.rs/serde_bytes
[`rmpv::Value`]: https://docs.rs/rmpv
[`serde_json::Value`]: https://docs.rs/serde_json/latest/serde_json/value

# Single-socket example
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
# use std::sync::Arc;
fn handler(socket: SocketRef, Data(data): Data::<Value>) {
// Emit a test message to the client
socket.emit("test", &data).ok();
// Emit a test message with multiple arguments to the client
socket.emit("test", &("world", "hello", 1)).ok();
// Emit a test message with an array as the first argument
let arr = [1, 2, 3, 4];
socket.emit("test", &[arr]).ok();
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |socket: SocketRef| {
socket.on("test", handler);
});
```

# Single-socket binary example with the `bytes` crate
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
# use std::sync::Arc;
# use bytes::Bytes;
fn handler(socket: SocketRef, Data(data): Data::<(String, Bytes, Bytes)>) {
// Emit a test message to the client
socket.emit("test", &data).ok();
// Emit a test message with multiple arguments to the client
socket.emit("test", &("world", "hello", Bytes::from_static(&[1, 2, 3, 4]))).ok();
// Emit a test message with an array as the first argument
let arr = [1, 2, 3, 4];
socket.emit("test", &[arr]).ok();
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |socket: SocketRef| {
socket.on("test", handler);
});
```

# Broadcast example
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
# use std::sync::Arc;
# use bytes::Bytes;
fn handler(socket: SocketRef, Data(data): Data::<(String, Bytes, Bytes)>) {
// Emit a test message in the room1 and room3 rooms, except for room2, with the received binary payload
socket.to("room1").to("room3").except("room2").emit("test", &data);

// Emit a test message with multiple arguments to the client
socket.to("room1").emit("test", &("world", "hello", 1)).ok();

// Emit a test message with an array as the first argument
let arr = [1, 2, 3, 4];
socket.to("room2").emit("test", &[arr]).ok();
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |s: SocketRef| s.on("test", handler));
```
96 changes: 96 additions & 0 deletions crates/socketioxide/docs/operators/emit_with_ack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Emit a message to the client and wait for one or more acknowledgments.

See [`emit()`](#method.emit) for more details on emitting messages.

The acknowledgment has a timeout specified in the config (5 seconds by default)
(see [`SocketIoBuilder::ack_timeout`]) or can be set with the [`timeout()`](#method.timeout) operator.

To receive acknowledgments, an [`AckStream`] is returned. It can be used in two ways:
* As a [`Stream`]: This will yield all the acknowledgment responses, along with the corresponding socket ID, received from the client. This is useful when broadcasting to multiple sockets and expecting more than one acknowledgment. To get the socket from this ID, use [`io::get_socket()`].
* As a [`Future`]: This will yield the first acknowledgment response received from the client, useful when expecting only one acknowledgment.

# Errors
If packet encoding fails, an [`EncodeError`] is **immediately** returned.

If the socket is full or if it is closed before receiving the acknowledgment,
a [`SendError::Socket`] will be **immediately** returned, and the value to send will be given back.

If the client does not respond before the timeout, the [`AckStream`] will yield
an [`AckError::Timeout`]. If the data sent by the client is not deserializable as `V`,
an [`AckError::Decode`] will be yielded.

[`SocketIoBuilder::ack_timeout`]: crate::SocketIoBuilder#method.ack_timeout
[`Stream`]: futures_core::stream::Stream
[`Future`]: futures_core::future::Future
[`AckError`]: crate::AckError
[`AckError::Decode`]: crate::AckError::Decode
[`AckError::Timeout`]: crate::AckError::Timeout
[`AckError::Socket`]: crate::AckError::Socket
[`AckError::Socket(SocketError::Closed)`]: crate::SocketError::Closed
[`SendError::Socket`]: crate::SendError::Socket
[`EncodeError`]: crate::EncodeError
[`io::get_socket()`]: crate::SocketIo#method.get_socket

# Single-socket example
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
# use std::sync::Arc;
async fn handler(socket: SocketRef, Data(data): Data::<Value>) {
// Emit a test message and wait for an acknowledgment with the timeout specified in the global config
match socket.emit_with_ack::<_, Value>("test", &data).unwrap().await {
Ok(ack) => println!("Ack received {:?}", ack),
Err(err) => println!("Ack error {:?}", err),
}
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |socket: SocketRef| {
socket.on("test", handler);
});
```

# Single-socket example with custom acknowledgment timeout
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
# use std::sync::Arc;
# use tokio::time::Duration;
async fn handler(socket: SocketRef, Data(data): Data::<Value>) {
// Emit a test message and wait for an acknowledgment with the timeout specified here
match socket.timeout(Duration::from_millis(2)).emit_with_ack::<_, Value>("test", &data).unwrap().await {
Ok(ack) => println!("Ack received {:?}", ack),
Err(err) => println!("Ack error {:?}", err),
}
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |socket: SocketRef| {
socket.on("test", handler);
});
```

# Broadcast example
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
# use futures_util::stream::StreamExt;
async fn handler(socket: SocketRef, Data(data): Data::<Value>) {
// Emit a test message in the room1 and room3 rooms,
// except for room2, with the binary payload received
let ack_stream = socket.to("room1")
.to("room3")
.except("room2")
.emit_with_ack::<_, String>("message-back", &data)
.unwrap();
ack_stream.for_each(|(id, ack)| async move {
match ack {
Ok(ack) => println!("Ack received, socket {} {:?}", id, ack),
Err(err) => println!("Ack error, socket {} {:?}", id, err),
}
}).await;
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |s: SocketRef| s.on("test", handler));
```
19 changes: 19 additions & 0 deletions crates/socketioxide/docs/operators/except.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Filter out all sockets selected with the previous operators that are in the specified rooms.

# Example
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
fn handler(socket: SocketRef, Data(data): Data::<Value>) {
// This message will be broadcast to all sockets in the namespace,
// except for those in room1 and the current socket
socket.broadcast().except("room1").emit("test", &data);
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |socket: SocketRef| {
socket.on("register1", |s: SocketRef| s.join("room1").unwrap());
socket.on("register2", |s: SocketRef| s.join("room2").unwrap());
socket.on("test", handler);
});
```
1 change: 1 addition & 0 deletions crates/socketioxide/docs/operators/get_socket.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Get a [`SocketRef`] by the specified [`Sid`].
14 changes: 14 additions & 0 deletions crates/socketioxide/docs/operators/join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Add all sockets selected with the previous operators to the specified room(s).

# Example
```rust
# use socketioxide::{SocketIo, extract::*};
fn handler(socket: SocketRef) {
// Add all sockets that are in room1 and room3 to room4 and room5
socket.within("room1").within("room3").join(["room4", "room5"]).unwrap();
let sockets = socket.within("room4").within("room5").sockets().unwrap();
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |s: SocketRef| s.on("test", handler));
```
14 changes: 14 additions & 0 deletions crates/socketioxide/docs/operators/leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Remove all sockets selected with the previous operators from the specified room(s).

# Example
```rust
# use socketioxide::{SocketIo, extract::*};
fn handler(socket: SocketRef) {
// Remove all sockets that are in room1 and room3 from room4 and room5
socket.within("room1").within("room3").leave(["room4", "room5"]).unwrap();
let sockets = socket.within("room4").within("room5").sockets().unwrap();
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |s: SocketRef| s.on("test", handler));
```
16 changes: 16 additions & 0 deletions crates/socketioxide/docs/operators/local.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Broadcast to all sockets only connected to this node.
When using the default in-memory adapter, this operator is a no-op.

# Example
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
fn handler(socket: SocketRef, Data(data): Data::<Value>) {
// This message will be broadcast to all sockets in this
// namespace that are connected to this node
socket.local().emit("test", &data);
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |s: SocketRef| s.on("test", handler));
```
14 changes: 14 additions & 0 deletions crates/socketioxide/docs/operators/rooms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Get all room names in the current namespace.

# Example
```rust
# use socketioxide::{SocketIo, extract::SocketRef};
fn handler(socket: SocketRef, io: SocketIo) {
println!("Socket connected to the / namespace with id: {}", socket.id);
let rooms = io.rooms().unwrap();
println!("All rooms in the / namespace: {:?}", rooms);
}

let (_, io) = SocketIo::new_svc();
io.ns("/", handler);
```
18 changes: 18 additions & 0 deletions crates/socketioxide/docs/operators/sockets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Get all sockets selected with the previous operators.

This can be used to retrieve any extension data (with the `extensions` feature enabled) from the sockets or to make certain sockets join other rooms.

# Example
```rust
# use socketioxide::{SocketIo, extract::*};
fn handler(socket: SocketRef) {
// Find extension data in each socket in the room1 and room3 rooms, except for room2
let sockets = socket.within("room1").within("room3").except("room2").sockets().unwrap();
for socket in sockets {
println!("Socket custom string: {:?}", socket.extensions.get::<String>());
}
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |s: SocketRef| s.on("test", handler));
```
31 changes: 31 additions & 0 deletions crates/socketioxide/docs/operators/timeout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Set a custom timeout when sending a message with an acknowledgement.

* See [`SocketIoBuilder::ack_timeout`](crate::SocketIoBuilder) for the default timeout.
* See [`emit_with_ack()`](#method.emit_with_ack) for more details on acknowledgements.

# Example
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
# use futures_util::stream::StreamExt;
# use std::time::Duration;
async fn handler(socket: SocketRef, Data(data): Data::<Value>) {
// Emit a test message in the room1 and room3 rooms, except for the room2
// room with the binary payload received, and wait for 5 seconds for an acknowledgement
socket.to("room1")
.to("room3")
.except("room2")
.timeout(Duration::from_secs(5))
.emit_with_ack::<_, Value>("message-back", &data)
.unwrap()
.for_each(|(id, ack)| async move {
match ack {
Ok(ack) => println!("Ack received from socket {}: {:?}", id, ack),
Err(err) => println!("Ack error from socket {}: {:?}", id, err),
}
}).await;
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |s: SocketRef| s.on("test", handler));
```
29 changes: 29 additions & 0 deletions crates/socketioxide/docs/operators/to.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Select all the sockets in the given rooms except for the current socket.

When called from a socket, if you want to also include it, use the [`within()`](#method.within) operator.

However, when called from the [`io`] (global context) level, there will be no difference.

[`io`]: crate::SocketIo

# Example
```rust
# use socketioxide::{SocketIo, extract::*};
# use serde_json::Value;
async fn handler(socket: SocketRef, io: SocketIo, Data(data): Data::<Value>) {
// Emit a message to all sockets in room1, room2, room3, and room4, except the current socket
socket
.to("room1")
.to(["room2", "room3"])
.emit("test", &data);

// Emit a message to all sockets in room1, room2, room3, and room4, including the current socket
io
.to("room1")
.to(["room2", "room3"])
.emit("test", &data);
}

let (_, io) = SocketIo::new_svc();
io.ns("/", |s: SocketRef| s.on("test", handler));
```
Loading

0 comments on commit 9a58244

Please sign in to comment.