Skip to content

Commit

Permalink
Fix docs (#20)
Browse files Browse the repository at this point in the history
* Fix docs

* fix oops
  • Loading branch information
paulgb authored May 3, 2024
1 parent 59e8371 commit 65dd74e
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 72 deletions.
64 changes: 30 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,48 @@ Let's implement a simple shared counter. Any connected client will be able to in
to every connected client.

```rust
impl SimpleStateroomService for SharedCounterServer {
fn new(_: &str,
_: &impl StateroomContext) -> Self {
SharedCounterServer(0)
use stateroom_wasm::*;

#[stateroom_wasm]
#[derive(Default)]
struct EchoServer;

impl StateroomService for EchoServer {
fn connect(&mut self, client_id: ClientId, ctx: &impl StateroomContext) {
ctx.send_message(client_id, format!("User {:?} connected.", client_id));
}

fn message(&mut self, _: ClientId,
message: &str,
ctx: &impl StateroomContext) {
match message {
"increment" => self.0 += 1,
"decrement" => self.0 -= 1,
_ => (),
}
fn message(&mut self, client_id: ClientId, message: MessagePayload, ctx: &impl StateroomContext) {
let Some(message) = message.text() else {
return;
};

ctx.send_message(
MessageRecipient::Broadcast,
&format!("new value: {}", self.0));
format!("User {:?} sent '{}'", client_id, message),
);
}

fn disconnect(&mut self, client_id: ClientId, ctx: &impl StateroomContext) {
ctx.send_message(
MessageRecipient::Broadcast,
format!("User {:?} left.", client_id),
);
}
}
```

To serve this service, we will compile it into a WebAssembly module. We import the `#[stateroom_wasm]`
annotation macro and apply it to the existing `SharedCounterServer` declaration.
annotation macro and apply it to the existing `SharedCounter` declaration.

```rust
use stateroom_wasm::stateroom_wasm;
use stateroom_wasm::*;

#[stateroom_wasm]
struct SharedCounterServer(i32);
#[derive(Default)]
struct SharedCounter(i32);

impl StateroomService for SharedCounter {}
```

Then, install the `stateroom` command-line tool and the `wasm32-wasi` target, and run
Expand Down Expand Up @@ -81,28 +93,12 @@ ws.send('increment')

If everything is set up correctly, the result will be printed out:

```
```text
new value: 1
```

If multiple clients are connected, each one will receive this message. Just like that, we have a mechanism for sharing some (very basic) application state between clients.

## Using without WebAssembly

If you don't want to compile your service to WebAssembly (for example, if you want to use
capabilities that are
not exposed by [WASI](https://wasi.dev/)), you can use `stateroom-server`.

```rust
use stateroom_server::*;

fn main() -> std::io::Result<()> {
serve::<SharedCounterServer>()?;

Ok(())
}
```

## Modules

Stateroom has a modular architecture. If all you want to do is generate a Stateroom service to
Expand All @@ -112,7 +108,7 @@ will probably be [`stateroom-cli`](/stateroom-cli), which provides a command-lin

- [`stateroom`](https://docs.rs/stateroom/) is the core, minimal implementation of the service interface.
- [`stateroom-cli`](https://docs.rs/stateroom-cli/) is a command-line interface for interacting with WebAssembly-compiled Stateroom services.
- [`stateroom-server`](https://docs.rs/stateroom-server/) provides [Actix](https://actix.rs/) actors to facilitate serving Stateroom services in a WebSocket server.
- [`stateroom-server`](https://docs.rs/stateroom-server/) provides an [Axum](https://github.com/tokio-rs/axum)-based WebSocket server that runs a Stateroom service.
- [`stateroom-wasm`](https://docs.rs/stateroom-wasm/) provides a macro for generating WebAssembly modules from Stateroom services.
- [`stateroom-wasm-host`](https://docs.rs/stateroom-wasm-host/) provides a way to import Stateroom services from WebAssembly modules.

Expand Down
12 changes: 4 additions & 8 deletions examples/binary-echo/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
use stateroom_wasm::{
stateroom_wasm, ClientId, MessageRecipient, StateroomContext, StateroomService, MessagePayload
};
use stateroom_wasm::*;

#[stateroom_wasm]
#[derive(Default)]
struct BinaryEcho;

impl StateroomService for BinaryEcho {
fn message(&mut self, _: ClientId, message: MessagePayload, ctx: &impl StateroomContext) {
let message = match message {
MessagePayload::Text(s) => MessagePayload::Bytes(s.as_bytes().to_vec()),
MessagePayload::Bytes(b) => MessagePayload::Text(format!("{:?}", b)),
};
ctx.send_message(MessageRecipient::Broadcast, message);
if let Some(message) = message.text() {
ctx.send_message(MessageRecipient::Broadcast, message);
}
}
}
2 changes: 1 addition & 1 deletion examples/clock/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use stateroom_wasm::{stateroom_wasm, MessageRecipient, StateroomContext, StateroomService};
use stateroom_wasm::*;

#[stateroom_wasm]
#[derive(Default)]
Expand Down
9 changes: 3 additions & 6 deletions examples/counter-service/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
use stateroom_wasm::{
stateroom_wasm, ClientId, MessageRecipient, StateroomContext, StateroomService, MessagePayload
};
use stateroom_wasm::*;

#[stateroom_wasm]
#[derive(Default)]
struct SharedCounterServer(i32);

impl StateroomService for SharedCounterServer {
fn message(&mut self, _: ClientId, message: MessagePayload, ctx: &impl StateroomContext) {
let message = match message {
MessagePayload::Text(s) => s,
MessagePayload::Bytes(_) => return,
let Some(message) = message.text() else {
return;
};

match &message[..] {
Expand Down
8 changes: 3 additions & 5 deletions examples/cpu-hog/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use stateroom_wasm::{
stateroom_wasm, ClientId, MessageRecipient, StateroomContext, StateroomService,
};
use stateroom_wasm::*;

// Seconds per nanosecond. (`wasi::clock_time_get` uses nanos.)
const SECONDS: u64 = 1_000_000_000;
Expand All @@ -15,7 +13,7 @@ fn get_time() -> u64 {

impl StateroomService for CpuHog {
fn connect(&mut self, _: ClientId, ctx: &impl StateroomContext) {
ctx.send_message(MessageRecipient::Broadcast, format!("Connected."));
ctx.send_message(MessageRecipient::Broadcast, "Connected.");

let init_time = get_time();
loop {
Expand All @@ -25,6 +23,6 @@ impl StateroomService for CpuHog {
}
}

ctx.send_message(MessageRecipient::Broadcast, format!("Finished."));
ctx.send_message(MessageRecipient::Broadcast, "Finished.");
}
}
9 changes: 3 additions & 6 deletions examples/echo-server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use stateroom_wasm::{
stateroom_wasm, ClientId, MessageRecipient, StateroomContext, StateroomService, MessagePayload,
};
use stateroom_wasm::*;

#[stateroom_wasm]
#[derive(Default)]
Expand All @@ -12,9 +10,8 @@ impl StateroomService for EchoServer {
}

fn message(&mut self, client_id: ClientId, message: MessagePayload, ctx: &impl StateroomContext) {
let message = match message {
MessagePayload::Text(s) => s,
MessagePayload::Bytes(b) => unimplemented!(),
let Some(message) = message.text() else {
return;
};

ctx.send_message(
Expand Down
9 changes: 3 additions & 6 deletions examples/randomness/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use bytemuck::cast;
use stateroom_wasm::{
stateroom_wasm, ClientId, MessageRecipient, StateroomContext, StateroomService, MessagePayload
};
use stateroom_wasm::*;

#[stateroom_wasm]
#[derive(Default)]
Expand All @@ -23,9 +21,8 @@ impl StateroomService for RandomServer {
}

fn message(&mut self, client_id: ClientId, message: MessagePayload, ctx: &impl StateroomContext) {
let message = match message {
MessagePayload::Text(s) => s,
MessagePayload::Bytes(_) => return,
let Some(message) = message.text() else {
return;
};

ctx.send_message(
Expand Down
17 changes: 11 additions & 6 deletions stateroom/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
//!
//! // Send a welcome message.
//! ctx.send_message(client,
//! &format!("Welcome to the chat! Your name is {}. \
//! format!("Welcome to the chat! Your name is {}. \
//! Send /nick <username> to change it.",
//! &username));
//!
//! // Alert all other connected users to the new user.
//! ctx.send_message(MessageRecipient::Broadcast,
//! &format!("{} has joined the chat", &username));
//! format!("{} has joined the chat", &username));
//! }
//!
//! /// This is called when a user disconnects.
Expand All @@ -39,21 +39,26 @@
//!
//! // Alert all remaining users that a user has left.
//! ctx.send_message(MessageRecipient::Broadcast,
//! &format!("{} has left the chat", &username));
//! format!("{} has left the chat", &username));
//! }
//!
//! /// This is called when a user sends a message.
//! fn message(&mut self, client: ClientId, message: &str, ctx: &impl StateroomContext) {
//! fn message(&mut self, client: ClientId, message: MessagePayload, ctx: &impl StateroomContext) {
//! let Some(message) = message.text() else {
//! // Ignore binary messages.
//! return;
//! };
//!
//! if let Some(new_nick) = message.strip_prefix("/nick ") {
//! // This message is a /nick command, so process accordingly.
//! let old_nick = self.client_to_nickname.insert(client, new_nick.to_string()).unwrap();
//! ctx.send_message(MessageRecipient::Broadcast,
//! &format!("{} is now known as {}", old_nick, new_nick));
//! format!("{} is now known as {}", old_nick, new_nick));
//! } else {
//! // Broadcast the message to all connected users, prefixed by the username.
//! let username = self.client_to_nickname.get(&client).unwrap();
//! ctx.send_message(MessageRecipient::Broadcast,
//! &format!("{}: {}", username, message));
//! format!("{}: {}", username, message));
//! }
//! }
//! }
Expand Down
34 changes: 34 additions & 0 deletions stateroom/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,46 @@ pub enum MessagePayload {
Text(String),
}

impl MessagePayload {
pub fn text(&self) -> Option<&str> {
match self {
MessagePayload::Text(s) => Some(s),
_ => None,
}
}

pub fn bytes(&self) -> Option<&[u8]> {
match self {
MessagePayload::Bytes(b) => Some(b),
_ => None,
}
}
}

impl Into<MessagePayload> for String {
fn into(self) -> MessagePayload {
MessagePayload::Text(self)
}
}

impl Into<MessagePayload> for &str {
fn into(self) -> MessagePayload {
MessagePayload::Text(self.to_string())
}
}

impl Into<MessagePayload> for Vec<u8> {
fn into(self) -> MessagePayload {
MessagePayload::Bytes(self)
}
}

impl Into<MessagePayload> for &[u8] {
fn into(self) -> MessagePayload {
MessagePayload::Bytes(self.to_vec())
}
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug)]
pub enum MessageToProcess {
Expand Down

0 comments on commit 65dd74e

Please sign in to comment.