Crate simple_json_server

Crate simple_json_server 

Source
Expand description

§Simple JSON Server

This library is intended to make it very easy to build a JSON-based server. Because its JSON it is easily called from multiple languages (think a TypeScript client). The goal is to provide a lot of functionality out of the box but with excellent ergonomics so that simple servers are easily built without almost any boilerplate.

The macro both creates your simple server (an actor) from an impl of your struct and also generates documentation for your actor so its 100% clear how to call it.

The way simple_json_server works is:

  1. Create a struct and create an impl block for it. The impl block should contain all the methods you want to expose on your server. The methods must be pub async fn and take &self as the first parameter. Others are ignored for this purpose.
  2. The #[actor] macro will generate the code to make your struct into an actor. Each method in turn gets turned into a JSON-RPC where:
  • the RPC name is the name of the method in your impl.
  • the parameters are JSONified parameters of the method. They are automatically turned into a JSON object with the name of the parameter as the key. The value is the JSON equivalent of the value of the parameter.
  • the return value is the JSONified return value of the method.
  • the macro will generate documentation for your actor so its 100% clear how to call it including the specific JSON payload needed to call the method.
  1. The create method will start a server for you and begin listening for incoming JSON-RPC calls via HTTP. create_ws will do the same but via WebSocket.

Note that the create and create_ws methods consume the actor. Here is an example with a simple “greet” method:

use simple_json_server::{Actor, actor};
#[derive(Debug, Clone)]
struct GreetActor;
#[actor]
impl GreetActor {
    pub async fn greet(&self, name: String) -> String {
        format!("Hello, {}!", name)
    }
}

#[tokio::main]
async fn main() {
    GreetActor.create(8080);
}

You can then (easily) invoke these methods in any way you make a web request. See examples/demo/src/main.rs for a complete example but for example:

# Call the 'greet' method
curl -X POST http://127.0.0.1:8080/greet -d '{"name": "World"}'

The approach is based on the Actor model of computation with some necessary deviations from the pure model. For those not familiar with actors, what that basically means is that:

  • State is not shared and the only way to communicate is through messages.
  • Message cannot contain pointers, etc. There is no shared memory (of course the internal implementation of an actor can be as complex as you like).
  • The actor is the only owner of its state. Other actors can send messages but cannot directly access the state.

For those very familiar with the actor model, the deviations are:

  • Rust itself isn’t functional in the same way as Lisp. So we eschew become as a keyword and generally find completely changing behavior is superfluous for general work.
  • Each actor is single threaded; we take this approach instead of using an atomic become to manage state changes.
  • Addresses are well known. This is critical to support cross language invocation (the classic client/server case) and contrasts from the Actor model where addresses are passed around.
  • RPC and return values are supported. Again, this is important to simplify cross language invocation and generally simplifies all distributed code at the cost of asynchronous execution and parallelism. In the future we’ll look to optimize cases that don’t need return values by not waiting for method completion.

§Examples

See the examples/ directory for more comprehensive examples:

# Basic calculator example
cargo run --example calculator

# HTTP and WebSocket server example
cargo run --example server

# A full example with docs
cd examples/demo
cargo run
cargo doc --open

Re-exports§

pub use tls::TlsConfig;

Modules§

tls

Traits§

Actor
The Actor trait must be implemented by all servers. Implementation is most commonly achieved by using the #[actor] macro with any other Rust struct and impl.

Attribute Macros§

actor
The #[actor] attribute macro that implements the Actor trait for a struct. This crate doesn’t make a lot of sense by itself - instead look at the simple_json_server crate which uses this macro.