toy-rpc Book

# Introduction

`toy-rpc` aims to be an easy-to-use `async` RPC tool that is inspired by golang's `net/rpc`'s API. It supports both `async_std` and `tokio` runtimes over either TCP or TLS. Integration with common HTTP server frameworks such as `actix_web`, `warp` and `tide` are provided.

The overall usage and API should feel similar to that of the golang's `net/rpc` package. Some of the names are changed to make them sound more "rusty". Because rust does not come with runtime reflection, attribute macros `#[export_impl]` and `#[export_trait]` / `#[export_trait_impl]`, and attribute `#[export_method]` are used to mark functions "exported" in golang's `net/rpc` perspective.

Minimum supported Rust version: 1.53 or later

## Why?

While there are grpc implementations like `grpc-rs` and `tonic` as well as schema-free crates like `tarpc`, I didn't find a crate that offers the same level of ease-of-use as that of the golang's `net/rpc` package. Other than the ease-of-use, not many async RPC crates work with both `async_std` and `tokio` runtime and could be difficult to integrate with the common async HTTP crates (`actix_web`, `warp`, and `tide`). Thus I started working on this crate to bring something that is easy-to-use and supports both `async_std` and `tokio` runtimes.

## Feature flags

Most of the feature flags can be put into three categories.

Choice of runtime and HTTP framework integration

- `async_std_runtime`: supports usage with `async-std`
- `tokio_runtime`: supports usage with `tokio`
- `http_tide`: enables `tide` integration on the server side. This also enables `async_std_runtime` and `ws_async_std`
- `http_actix_web`: enables `actix-web` integration on the server side. This also enables `tokio_runtime` and `ws_tokio`
- `http_warp`: enables integration with `warp` on the server side. This also enables `tokio_runtime` and `ws_tokio`
- `http_axum`: enables integration with `axum` on the server side. This also enables `tokio_runtime` and `ws_tokio`

Choice of RPC server or client (both can be enabled at the same time)

- `server`: enables RPC server
- `client`: enables RPC client. Please note that `ws` must also be enabled for client to use `dial_http(addr)` or `dial_websocket(addr)`.

Choice of serialization/deserialzation (only one should be enabled at a time)

- `serde_bincode`: (default) the default codec will use `bincode` for serialization/deserialization
- `serde_json`: the default codec will use `serde_json` for `json` serialization/deserialization
- `serde_cbor`: the default codec will use `serde_cbor` for serialization/deserialization
- `serde_rmp`: the default codec will use `rmp-serde` for serialization/deserialization

WebSocket support (HTTP integration is implemented using WebSocket)

- `ws_tokio`: enables WebSocket and HTTP integrations with `tokio`. This must be enabled for client to use `dial_http(addr)` or `dial_websocket(addr)` with `tokio_runtime`.
- `ws_async_std`: enables WebSocket and HTTP integrations with `async-std`. This must be enabled for client to use `dial_http(addr)` or `dial_websocket(addr)` with `async_std_runtime`.

TLS support

- `tls`: enables TLS support

Convenience conversion to `anyhow::Error`

- `anyhow`: enables using `anyhow::Error` in RPC methods

Other trivial feature flags are listed below, and they are likely of no actual usage for you.

- `docs`
- `std`: `serde/std`. There is no actual usage right now.

By default, only `serde_bincode` feature is enabled. You must enable at least one runtime feature flag and the `server` and/or `client` to have something usable.

## Default features

    default = [
        "serde_bincode",
    ]

# Quickstart

A simple quickstart with `tokio` runtime is shown below. More examples can be found in the Example chapter.

## Initialize new project

`cargo new --lib toy_rpc_quickstart`

## Add dependencies

    [dependencies]
    tokio = { version = "1", features = ["rt-multi-thread", "macros", "net"] }
    toy-rpc = { version = "0.7.5", features = ["tokio_runtime", "server", "client"] }

## Project structure

    ./src
    ├── /bin
    │   ├── server.rs
    │   ├── client.rs
    └── lib.rs

In the `Cargo.toml`, you may need to specify the binaries with

    [[bin]]
    name = "server"
    path = "src/bin/server.rs"

    [[bin]]
    name = "client"
    path = "src/bin/client.rs"

## Define RPC service

In `src/lib.rs`

    // src/lib.rs

    pub mod rpc {
        use toy_rpc::macros::export_impl;
        pub struct Echo { }

        #[export_impl]
        impl Echo {
            #[export_method]
            pub async fn echo_i32(&self, arg: i32) -> Result<i32, String> {
                Ok(arg)
            }
        }
    }

## RPC server

In `src/bin/server.rs`

    // src/bin/server.rs

    use tokio::{task, net::TcpListener};
    use std::sync::Arc;
    use toy_rpc::Server;

    use toy_rpc_quickstart::rpc::Echo;

    #[tokio::main]
    async fn main() {
        let addr = "127.0.0.1:23333";

        // Creates an instance of the `Echo` service
        let echo_service = Arc::new(
            Echo { }
        );

        let server = Server::builder()
            .register(echo_service) // register service
            .build();
        let listener = TcpListener::bind(addr).await.unwrap();

        // Run the server in a separate task
        let handle = task::spawn(async move {
            println!("Starting server at {}", &addr);
            server.accept(listener).await.unwrap();
        });
        handle.await.expect("Error running the RPC server");
    }

## RPC client

In `src/bin/client.rs`

    use toy_rpc::Client;

    // import everything including the client stub generated by the macro
    use toy_rpc_quickstart::rpc::*;

    #[tokio::main]
    async fn main() {
        let addr = "127.0.0.1:23333";
        let client = Client::dial(addr).await.unwrap();

        let result: i32 = client
            .echo() // calling service `Echo`
            .echo_i32(3i32) // calling RPC method `echo_i32`
            .await
            .unwrap();
        println!("{:?}", result);

        // The above call is equivalent to
        let result: i32 = client
            .call("Echo.echo_i32", 3i32)
            .await
            .unwrap();
        println!("{:?}", result);
    }

# Define Service

There are two ways (but three attribute macros) to help you define the the RPC service.

- The attribute macro `#[export_impl]` can be used when the implementation and the service definition are located in the same file/project. The name of the `struct` will be used as the default service name with `#[export_impl]` in a case sensitive manner.
- The attribute macros `#[export_trait]` and `#[export_trait_impl]` should be used when an abstract service definition (a `trait`) will be shared among different projects. The name of the `trait` will be used as the default service name with `#[export_trait]` in a case sensitive manner.

Both `#[export_impl]` and `#[export_trait]` will generate the client stub traits/methods when the "client" feature flag is enabled and a runtime feature flag is enabled on `toy-rpc`.

Inside the `impl` block or `trait` definition block, you should then use the attribute `#[export_method]` to mark which method(s) should be "exported" as RPC method(s). The methods to export must meet the following criteria on the server side

- the method resides in an impl block marked with `#[export_impl]` or `#[export_trait]`
- the method is marked with `#[export_method]` attribute
- the method takes one argument other than `&self` and returns a `Result<T, E>`
  - the argument must implement trait `serde::Deserialize`
  - the `Ok` type `T` of the result must implement trait `serde::Serialize`
  - the `Err` type `E` of the result must implement trait `ToString`

The method is essentially in the form

    #[export_method]
    async fn method_name(&self, args: Req) -> Result<Res, ErrorMsg>
    where
        Req: serde::Deserialize,
        Res: serde::Serialize,
        ErrorMsg: ToString,
    {
        // ...
    }

## Example Usage

Use the following dependencies to work with the examples below

    [dependencies]
    async-trait = "0.1.50"
    toy-rpc = "0.7.5"

### `#[export_impl]`

When you have both the service definition and the implementation in the same file, you can use `#[export_impl]` on the `impl` block. This will also use the name of the `struct` as the default service name.

File structure

    ./src
    ├── /bin
    │   ├── server.rs
    │   ├── client.rs
    └── lib.rs

Suppose that both the service definitions and implementations are placed in `src/lib.rs`.

    // src/lib.rs
    use toy_rpc::macros::export_impl;

    pub struct Foo { }

    #[export_impl] // The default service name will be "Foo"
    impl Foo {
        // use attribute `#[export_method]` to mark which method to "export"
        #[export_method]
        async fn exported_method(&self, args: ()) -> Result<String, String> {
            Ok("exported method".into())
        }

        async fn not_exported_method(&self, args: ()) -> Result<String, String> {
            Ok("not exported method".into())
        }
    }

You may also define a separate trait in `src/lib.rs` which is implemented by some `struct` in the same file.

    // continuing in src/lib.rs

    use async_trait::async_trait;

    #[async_trait]
    pub trait Arith {
        async fn add(&self, args: (i32, i32)) -> Result<i32, String>;
        async fn subtract(&self, args: (i32, i32)) -> Result<i32, String>;
    }

    pub struct Bar { }
    // implement the Arith trait for `Bar { }`and then mark the implementation as "exported" for RPC

    #[async_trait]
    // Place `#[export_impl] ` after `#[async_trait]`
    #[export_impl] // The default service name will be "Bar"
    impl Arith for Bar {
        // Only mark `add(...)` as RPC method
        #[export_method]
        async fn add(&self, args: (i32, i32)) -> Result<i32, String> {
            Ok(args.0 + args.1)
        }

        // `subtract(...)` will not be accessible from RPC calls
        async fn subtract(&self, args: (i32, i32)) -> Result<i32, String> {
            Ok(args.0 - args.1)
        }
    }

We will continue to use this example in the Server and Client chapters.

### `#[export_trait]` and `#[export_trait_impl]`

When you want the abstract service definition to be shared but without concreate implementations, you should use `#[export_trait]` on the trait definition and `#[export_trait_impl]` on the concrete trait implementation. Please note that the default service name hence will be the name of the `trait` NOT that of the `struct`.

Suppose we will have three separate crates

- `"example-service"` as the service definition,
- `"example-server"` acting as the server,
- and `"example-client"` acting as the client

which can also be found in the GitHub examples (service, server, client).

We are going to define the RPC service just as a trait in `"example-service"`.

    // example-service/src/lib.rs
    use async_trait::async_trait;
    use toy_rpc::macros::export_trait;

    #[async_trait]
    #[export_trait] // The default service name will be "Arith"
    pub trait Arith {
        // let's mark both `add(...)` and `subtract(...)` as RPC method
        #[export_method]
        async fn add(&self, args: (i32, i32)) -> Result<i32, String>;

        #[export_method]
        async fn subtract(&self, args: (i32, i32)) -> Result<i32, String>;

        // some method that we don't want to export
        fn say_hi(&self);
    }

We will continue to use this example in the Server and Client chapters.

If you want to the `Client` implements the RPC trait and don't care about cancelling the RPC call, the implementation can be conveniently generated with an argument `(impl_for_client)` in the attribute macro `#[export_trait]`. The usage will demonstrated below.

    #![allow(unused)]
    fn main() {
    #[async_trait]
    #[export_trait(impl_for_client)] // This will generate `Arith` trait implementation for `toy_rpc::Client`
    pub trait Arith {
        // let's mark both `add(...)` and `subtract(...)` as RPC method
        #[export_method]
        async fn add(&self, args: (i32, i32)) -> Result<i32, anyhow::Error>;

        #[export_method]
        async fn subtract(&self, args: (i32, i32)) -> Result<i32, toy_rpc::Error>;

        // All methods must be exported if trait implementation generation is enabled
        // fn say_hi(&self);
    }
    }

This will allow convenient client usage like

    #![allow(unused)]
    fn main() {
    // client.rs

    let reply = Arith::add(&client, (3,4)).await.unwrap();
    }

More can be found here in the `tokio_tcp` example

# Server

The server API look straightforward and fairly close to that of golang's `net/rpc` package. First we should choose an async runtime and enable the `"server"` feature flag in our `Cargo.toml` file. Here, we will choose the `tokio` runtime be enabling the `"tokio_runtime"` feature flag.

    [dependencies]
    tokio = { version = "1", features = ["rt-multi-thread", "macros", "net"] }
    toy-rpc = { version = "0.7.5", features = ["tokio_runtime", "server"] }

The next step is then to build the service instances and register the service instances onto a server which will be demonstrated below.

You can definitely register services that are defined with `#[export_impl]` and `#[export_trait]` on the same server. However, for simplicity and consistency with the service definition chapter, in the discussions below, we are going to have separate examples. If you need an example where services defined with `#[export_impl]` and `#[export_trait]` are all registered on the same server, please refer to this.

## Example with `#[export_impl]`

Let's just remind ourselves that in this example the service definition and implementation are located in the same file/project, and the file structure is as follows

    ./src
    ├── /bin
    │   ├── server.rs
    │   ├── client.rs
    └── lib.rs

Using the service we have defined and implemented in the previous chapter, we are going instantiate those services and register those instances in `src/bin/server.rs`.

    // src/bin/server.rs
    use tokio::net::TcpListener;
    use std::sync::Arc;
    use toy_rpc::Server;

    // Suppose the name of this crate is "example"
    // Let's import the two services
    use example::{Foo, Bar};

    #[tokio::main]
    async fn main() {
        let addr = "127.0.0.1:23333";
        let foo = Arc::new(Foo { }); // instance of "Foo" service
        let bar = Arc::new(Bar { }); // instance of "Bar" service

        // Register the services onto a server
        let server = Server::builder()
            .register(foo) // register service instance with default name "Foo"
            .register(bar) // register service instance with default name "Bar"
            .build(); // build the server

        // Open a TcpListener for incoming connections
        let listener = TcpListener::bind(addr).await.unwrap();

        // Start our server and accept incoming connections and requests
        server.accept(listener).await.unwrap();
    }

## Example with `#[export_trait]` and `#[export_trait_impl]`

Now we are back with the example with `#[export_trait]` and `#[export_trait_impl]`, let's just remind ourselves that we have three separate crates where the service is defined in `"example-service"` and will be implemented in `"example-server"` as shown below.

We will continue to use the `tokio` runtime so that we don't need to change our `Cargo.toml` file, but because we will be implementing the service below, we will need to add `async-trait` and our service definition crate into our dependencies.

    [dependencies]
    async-trait = "0.1.50"
    tokio = { version = "1", features = ["rt-multi-thread", "macros", "net"] }
    toy-rpc = { version = "0.7.5", features = ["tokio_runtime", "server"] }

    # our service definition
    example-service = { version = "0.1.0", path = "../example-service" }

Now, let's implement the service and start the server

    // src/main.rs

    use tokio::net::TcpListener;
    use std::sync::Arc;
    use async_trait::async_trait;
    use toy_rpc::Server;
    use toy_rpc::macros::export_trait_impl;

    // Import our service definition
    // Make sure you import everything to include the auto-generated helper
    // traits that allow convenient service registration
    use example_service::*;

    struct Abacus { }

    #[async_trait]
    #[export_trait_impl] // The default service name will be "Arith"
    impl Arith for Abacus {
        // Please note that you do NOT need to mark methods with
        // `#[export_method]` another time

        async fn add(&self, args: (i32, i32)) -> Result<i32, String> {
            Ok(args.0 + args.1)
        }

        async fn subtract(&self, args: (i32, i32)) -> Result<i32, String> {
            Ok(args.0 - args.1)
        }

        fn say_hi(&self) {
            println!("hi");
        }
    }

    #[tokio::main]
    async fn main() {
        let addr = "127.0.0.1:23333";
        let arith = Arc::new(Abacus{}); // create an instance of the `Arith` service
        let listener = TcpListener::bind(addr).await.unwrap();
        let server = Server::builder()
            .register(arith) // register service with default name "Arith"
            .build();

        println!("Starting server at {}", &addr);
        server.accept(listener).await.unwrap()
    }

# Server with `async-std` runtime

The overall usage with `async-std` runtime should look (almost) the same as the examples before. The only difference would be

1. add `async_std` to your dependecies
2. enable feature `"async_std_runtime"` as opposed to `"tokio_runtime"`
3. use `async_std::net::TcpListener` instead of `tokio::net::TcpListener`

If we were to run the server with `async-std` runtime, our previous `#[export_trait]` and `#[export_trait_impl]` example would then look like the code below.

    [dependencies]
    async-trait = "0.1.50"
    async-std = { version = "1.9.0", features = ["attributes"] }
    toy-rpc = { version = "0.7.5", features = ["async_std_runtime", "server"] }

    # our service definition
    example-service = { version = "0.1.0", path = "../example-service" }

    // src/main.rs

    use async_std::net::TcpListener;
    use std::sync::Arc;
    use async_trait::async_trait;
    use toy_rpc::Server;
    use toy_rpc::macros::export_trait_impl;

    // Import our service definition
    // Make sure you import everything to include the auto-generated helper
    // traits that allow convenient service registration
    use example_service::*;

    struct Abacus { }

    #[async_trait]
    #[export_trait_impl] // The default service name will be "Arith"
    impl Arith for Abacus {
        // Please note that you do NOT need to mark methods with
        // `#[export_method]` another time

        async fn add(&self, args: (i32, i32)) -> Result<i32, String> {
            Ok(args.0 + args.1)
        }

        async fn subtract(&self, args: (i32, i32)) -> Result<i32, String> {
            Ok(args.0 - args.1)
        }

        fn say_hi(&self) {
            println!("hi");
        }
    }

    #[async_std::main]
    async fn main() {
        let addr = "127.0.0.1:23333";
        let arith = Arc::new(Abacus{}); // create an instance of the `Arith` service
        let listener = TcpListener::bind(addr).await.unwrap();
        let server = Server::builder()
            .register(arith) // register service with default name "Arith"
            .build();

        println!("Starting server at {}", &addr);
        server.accept(listener).await.unwrap()
    }

# Server with `tokio` runtime

This is just going to be an identical copy of the previous `#[export_trait]` and `#[export_trait_impl]` example.

    [dependencies]
    async-trait = "0.1.50"
    tokio = { version = "1", features = ["rt-multi-thread", "macros", "net"] }
    toy-rpc = { version = "0.7.5", features = ["tokio_runtime", "server"] }

    # our service definition
    example-service = { version = "0.1.0", path = "../example-service" }

    // src/main.rs

    use tokio::net::TcpListener;
    use std::sync::Arc;
    use async_trait::async_trait;
    use toy_rpc::Server;
    use toy_rpc::macros::export_trait_impl;

    // Import our service definition
    // Make sure you import everything to include the auto-generated helper
    // traits that allow convenient service registration
    use example_service::*;

    struct Abacus { }

    #[async_trait]
    #[export_trait_impl] // The default service name will be "Arith"
    impl Arith for Abacus {
        // Please note that you do NOT need to mark methods with
        // `#[export_method]` another time

        async fn add(&self, args: (i32, i32)) -> Result<i32, String> {
            Ok(args.0 + args.1)
        }

        async fn subtract(&self, args: (i32, i32)) -> Result<i32, String> {
            Ok(args.0 - args.1)
        }

        fn say_hi(&self) {
            println!("hi");
        }
    }

    #[tokio::main]
    async fn main() {
        let addr = "127.0.0.1:23333";
        let arith = Arc::new(Abacus{}); // create an instance of the `Arith` service
        let listener = TcpListener::bind(addr).await.unwrap();
        let server = Server::builder()
            .register(arith) // register service with default name "Arith"
            .build();

        println!("Starting server at {}", &addr);
        server.accept(listener).await.unwrap()
    }

# Integrations with HTTP Server

HTTP integration is accomplished with WebSocket, and (currently) integration with three common `rust` HTTP server are supported by enabling the corresponding feature flag.

- `actix-web`: `"http_actix_web"`
- `tide`: `"http_tide"`
- `warp`: `"http_warp"`

# HTTP integration with `actix-web`

The `"http_actix_web"` feature flag should be toggled on to enable HTTP integration with `actix-web`. Enabling `"http_actix_web"` feature flag will also enable the `"tokio_runtime"` feature flag.

A convenience method `Server::handle_http()` is available when `"http_actix_web"` is the only enabled http integration feature flag. If you have multiple http integration feature flags enabled, you can use the `Server::scope_config` method instead (please note that you should use `scope_config` without brackets).

We will demonstrate the usage with a new example.

    [dependencies]
    actix-web = "3.3.2"
    toy-rpc = { version = "0.7.5", features = ["http_actix_web", "server"] }

    use std::sync::Arc;
    use toy_rpc::macros::export_impl;
    use toy_rpc::Server;
    use actix_web::{web, HttpServer, App};

    pub struct Calculator { }

    #[export_impl]
    impl Calculator {
        #[export_method]
        async fn multiply(&self, args(i32, i32)) -> Result<i32, String> {
            Ok(args.0 * args.1)
        }

        #[export_method]
        async fn divide(&self, args(i32, i32)) -> Result<i32, String> {
            Ok(args.0 / args.1)
        }
    }

    #[actix_web::main]
    async fn main() {
        let addr = "127.0.0.1:23333";
        let calculator = Arc::new(Calculator { });
        let server = Server::builder()
            .register(calculator)
            .build();

        let app_data = web::Data::new(server);

        HttpServer::new(
            move || {
                App::new()
                    .service(
                        web::scope("/rpc/")
                            .app_data(app_data.clone())
                            .configure(Server::handle_http()) // equivalent to the line below
                            // .configure(Server::scope_config)
                    )
            }
        )
        .bind(addr).unwrap()
        .run()
        .await;
    }

# HTTP integration with `actix-web`

The `"http_tide"` feature flag should be toggled on to enable HTTP integration with `tide`. Enabling `"http_tide"` feature flag will also enable the `"async_std_runtime"` feature flag.

A convenience method `Server::handle_http(self)` is available when `"http_tide"` is the only http integration flag that is enabled. If multiple http integration feature flags are enabled, you can use `Server::into_endpoint(self)` method instead.

We will demonstrate the usage with a new example.

    [dependencies]
    tide = "0.16.0"
    async-std = { version = "1", features = [ "attributes", ] }
    toy-rpc = { version = "0.7.5", features = ["http_tide", "server"] }

    use std::sync::Arc;
    use toy_rpc::macros::export_impl;
    use toy_rpc::Server;
    use tide::prelude::*;

    pub struct Calculator { }

    #[export_impl]
    impl Calculator {
        #[export_method]
        async fn multiply(&self, args(i32, i32)) -> Result<i32, String> {
            Ok(args.0 * args.1)
        }

        #[export_method]
        async fn divide(&self, args(i32, i32)) -> Result<i32, String> {
            Ok(args.0 / args.1)
        }
    }

    #[async_std::main]
    async fn main() {
        // Get the RPC server ready
        let addr = "127.0.0.1:23333";
        let calculator = Arc::new(Calculator { });
        let server = Server::builder()
            .register(calculator)
            .build();

        // Now we will work with `tide` HTTP server
        let mut app = tide::new();
        app.at("/rpc/").nest(server.handle_http());
        app.listen(addr).await.unwrap();
    }

# HTTP integration with `actix-web`

The `"http_warp"` feature flag should be toggled on to enable HTTP integration with `warp` crate. Enabling `"http_warp"` feature flag will also enable the `"tokio_runtime"` featrue flag.

A convenience method `Server::handle_http(self)` is available when `"http_warp"` is the only enabled http integration feature flag. If you have multiple http integration flags enabled, you can use the `Server::into_boxed_filter(self)` method instead.

We will demonstrate the usage with a new example.

    [dependencies]
    warp = "0.3.0"
    tokio = { version = "1.4.0", features = ["rt-multi-thread", "macros"] }
    toy-rpc = { version = "0.7.5", features = ["http_warp", "server"] }

    use std::sync::Arc;
    use toy_rpc::macros::export_impl;
    use toy_rpc::Server;

    pub struct Calculator { }

    #[export_impl]
    impl Calculator {
        #[export_method]
        async fn multiply(&self, args(i32, i32)) -> Result<i32, String> {
            Ok(args.0 * args.1)
        }

        #[export_method]
        async fn divide(&self, args(i32, i32)) -> Result<i32, String> {
            Ok(args.0 / args.1)
        }
    }

    #[tokio::main]
    async fn main() {
        let calculator = Arc::new(Calculator { });
        let server = Server::builder()
            .register(calculator)
            .build();

        // Serve RPC at "ws://127.0.0.1/rpc/"
        // (there is a "_rpc_" appended to the end of the path but the client takes care of that)
        let routes = warp::path("rpc")
            .and(server.handle_http());
        warp::serve(routes).run(([127, 0, 0, 1], 23333)).await;
    }

# Client

The client side usage should feel fairly close to that of the golang's `net/rpc` package as well except some changes that makes the client API more rusty and async.

To connect a client to a "raw" TCP server, you should use the `dial` function; to connect to a HTTP server, you should then use the `dial_http` function. Once you have a connected client, you can then use the `call_blocking` and `call` methods to access the RPC functions on the server. The `#[export_impl]` and `#[export_trait]` attribute macros also generates client stub functions that allows the client to conveniently access the RPC functions without worrying about typing the wrong service or method name.

# Connecting to TCP server / HTTP server

For the examples below, we will assume running with the `tokio` runtime.

    [dependencies]
    tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
    toy-rpc = { version = "0.7.5", features = ["tokio_runtime", "client"] }

The example below shows how to connect to a TCP server

    use toy_rpc::Client;

    #[tokio::main]
    async fn main() {
        let client = Client::dial("127.0.0.1:23333").await
            .expect("Failed to connect to server");
    }

Connecting to the HTTP server looks very similar with some minor changes. The example below assumes that we are trying to connect to one of the HTTP servers (all three HTTP integration examples have the RPC server serving at `"ws://127.0.0.1:23333/rpc/"`). Please note that there is a "rpc" appended to the end of the path by the server integration methods, but this is automatically handled by the `Client::dial_http` method so you don't need to worry about that.

    use toy_rpc::Client;

    #[tokio::main]
    async fn main() {
        let client = Client::dial_http("ws://127.0.0.1:23333/rpc/").await
            .expect("Failed to connect to server");
    }

# Accessing RPC services

## `call_blocking(...)` and `call(...)`

There are two methods available for accessing the RPC services and methods. The `call_blocking` method blocks the execution until the response is received, and the `call` method is the asynchronous version where the execution will yield to other tasks scheduled by the runtime while waiting for the response. Cancellation is also supported on the `call` method, which is discussed with more details in the next chapter.

(Support of timeout is still work-in-progress. The book will be updated once the feature is implemented.)

## Generated client stub functions

The generated client stub functions internally uses the `call(...)` method and are thus async. The client stub functions consist of two steps. The first step is to access your service, and the second step is to access the method defined in that particular service. The method in the first step is always the name of the service but in snake case. For example, if you have a service `struct FooBar { }`, then the client method you use to access the service will be `foo_bar()`. The client method you use to access the method is identical to the method definition in the RPC service. For example, if an RPC method is defined as `fn add(&self, args(i32, i32)) -> Result<i32, String>;`, then the client method you use would be `client.bar().add((3i32, 4i32)).await;`

## Examples

We will continue the `#[export_impl]` example and the [`#[export_trait]` and `#[export_trait_impl]`] example to demonstrate how to access RPC service on the server. The methods you will use to access the RPC service are the same for a TCP connection and a HTTP connection, and for simplicity, all the examples below will assume a TCP connection. For more examples on use with HTTP connections, please checkout the GitHub examples.

### `#[export_impl]`

Let's just remind ourselves that in this example the service definition and implementation are located in the same file/project, and the file structure is as follows

    ./src
    ├── /bin
    │   ├── server.rs
    │   ├── client.rs
    └── lib.rs

Since the service is defined and implemented in `src/lib.rs` from the previous chapter, we are going to include everything in the `src/lib.rs` file to allow us use the generated client stub functions in our `src/bin/client.rs`.

    use toy_rpc::Client;
    use toy_rpc::Error;

    // include everything from the lib.rs file
    // assuming the name of the crate is "example"
    use example::*;

    #[tokio::main]
    async fn main() {
        let client = Client::dial("127.0.0.1:23333").await
            .expect("Failed to connect to the server");

        // Access the remote `exported_method` method of `Foo` service in a blocking manner
        let result: Result<String, Error> = client.call_blocking("Foo.exported_method", ());
        println!("{:?}", result);

        // Access the remote `add` method of `Bar` service in an asynchronous manner
        let result: Result<i32, Error> = client.call("Bar.add", (3i32, 4i32)).await;
        println!("{:?}", result);

        // You can also use the generated client stub functions
        // Access the remote `exported_method` method of `Foo` service
        // using the generated client stub functions
        let result = client
            .foo() // access `Foo` service
            .exported_method(()) // access `exported_method` of the `Foo` service
            .await;
        println!("{:?}", result);

        // Access the remote `add` method of `Bar` service
        // using the generated client stub functions
        let result = client
            .bar()
            .add((3, 4))
            .await;
        println!("{:?}", result);
    }

### `#[export_trait]` and `#[export_trait_impl]`

Again, let's just remind ourselves that the service in this example is defined in a separate crate `example-service` and the service is implemented and served by `example-server` crate. What we will be doing below is to demonstrate the client, which, not surprisingly, look pretty much the same as the example above.

Because the service definition resides in a separate crate, we will need to import that crate as well.

    [dependencies]
    tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
    toy-rpc = { version = "0.7.5", features = ["tokio_runtime", "client"] }

    # import our service definition, assuming we have this definition at "../example-service"
    example-service = { version = "0.1.0", path = "../example-service" }

    use toy_rpc::{Client, Error};

    // import everything to use the generated client stub functions
    use example_service::*;

    #[tokio::main]
    async fn main() {
        let client = Client::dial("127.0.0.1:23333").await
            .expect("Failed to connect to the server");

        // Access the remote method `add` of service `Arith` in a blocking manner
        let result: Result<i32, Error> = client.call_blocking("Arith.add", (3i32, 4i32));
        println!("{:?}", result);

        // Access the remote method `subtract` of service `Arith` in an asynchronous manner
        let result: Result<i32, Error> = client.call("Arith.subtract", (9i32, 6i32)).await;
        println!("{:?}", result);

        // Let's use the generated client stub functions
        let result = client
            .arith() // access `Arith` service
            .add((3i32, 4i32)) // access `add` method
            .await;
        println!("{:?}", result);

        let result = client
            .arith() // access `Arith` service
            .subtract((9i32, 6i32)) // access `subtract` method
            .await;
        println!("{:?}", result);
    }

# Cancellation of RPC call

Cancellation is supported starting from version 0.7.0-alpha.2. The client method `call(...)` returns a type `Call`, which can be either `.await`ed for the response or `cancel()`ed to stop the execution. When an RPC request is started with the method `call(...)`, the request is sent by a background task whether or not the `Call` is `.await`ed. Upon `cancel()`, the client will send a cancellation request to the server; however, it should be noted that if the client is dropped immediately after calling `cancel()`, the server may not be able to receive the cancellation request before the connection is dropped by the client.

Below is a simple example showing cancellation on the `tokio` runtime.In this example, we are going to define a new service with a method that simply runs in loop and sleep for a certain period of time.

File structure:

    ./src
    ├── /bin
    │   ├── server.rs
    │   ├── client.rs
    └── lib.rs

Add dependencies:

    [dependencies]
    tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }
    toy-rpc = { version = "0.7.5", features = ["tokio_runtime", "server", "client"] }

Service definition and implementation placed in `src/lib.rs`:

    use std::time::Duration;
    use toy_rpc::macros::export_impl;
    use tokio::time;

    struct Example { }

    #[export_impl]
    impl Example {
        #[export_method]
        async fn finite_loop(&self, args: ()) -> Result<(), String> {
            for counter in 0..500 {
                time::sleep(Duration::from_millis(500)).await;
            }
        }

        #[export_method]
        async fn echo(&self, args: String) -> Result<String, String> {
            Ok(args)
        }
    }

Serve the RPC service with `src/bin/server.rs`

    use std::sync::Arc;
    use toy_rpc::Server;
    use tokio::net::TcpListener;

    // assume the name of the crate is "example"
    // import service definition and implementation
    use example::Example;

    #[tokio::main]
    async fn main() {
        let addr = "127.0.0.1:23333";
        let ex = Arc::new(Example { });
        let server = Server::builder()
            .register(ex)
            .build()

        let listener = TcpListener::bind(addr).await.unwrap();
        server.accept(listener).await.unwrap();
    }

On the client side, let's call the `finite_loop` RPC function and wait for two seconds and cancel it.

    use std::time::Duration;
    use tokio::time;
    use toy_rpc::client::{Client, Call};

    // assume the name of the crate is "example"
    // import service definitions and generated client stub functions
    use example::*;

    #[tokio::main]
    async fn main() {
        let client = Client::dial("127.0.0.1:23333").await
            .expect("Failed to connect to server");

        let call: Call<()> = client
            .example() // access `Example` service
            .finite_loop(()); // access `finite_loop` method

        // wait for 2 seconds and cancel
        time::sleep(Duration::from_secs(2)).await;
        call.cancel();
        // You can still .await on a canceled Call, but this will return an error
        let reply = call.await;
        println!("{:?}", reply); // Err(Error::Canceled(Some(id)))

        // the `Call` type can be `.await`ed to wait for the response
        let call = client
            .example() // access `Example` service
            .echo("hello world".to_string()); // access `echo` method
        let result = call.await;
        println!("{:?}", result);
    }

# Set timeout for an RPC call

A default timeout of 10 seconds is assigned to client upon creation, and this default timeout can be changed using `set_default_timeout(duration: Duration)` method on the client. Each RPC call will have the default timeout unless it is prepended with `set_next_timeout(..)` method. The client can set the timeout for the next RPC request using the `set_next_timeout(duration: Duration)` method, which can be chained with the `call` method. Please note that the timeout is ONLY set for the immediate next RPC call, and all RPC calls do not have timeout if not explicitly set using the `timeout` method.

We will re-use the example service definition in the cancellation chapter. For you convenience, the service definition and server code are copied below.

File structure:

    ./src
    ├── /bin
    │   ├── server.rs
    │   ├── client.rs
    └── lib.rs

Add dependencies:

    [dependencies]
    tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }
    toy-rpc = { version = "0.7.5", features = ["tokio_runtime", "server", "client"] }

Service definition and implementation placed in `src/lib.rs`:

    use std::time::Duration;
    use toy_rpc::macros::export_impl;
    use tokio::time;

    struct Example { }

    #[export_impl]
    impl Example {
        #[export_method]
        async fn finite_loop(&self, args: ()) -> Result<(), String> {
            for counter in 0..500 {
                time::sleep(Duration::from_millis(500)).await;
            }
        }

        #[export_method]
        async fn echo(&self, args: String) -> Result<String, String> {
            Ok(args)
        }
    }

Serve the RPC service with `src/bin/server.rs`.

    use std::sync::Arc;
    use toy_rpc::Server;
    use tokio::net::TcpListener;

    // assume the name of the crate is "example"
    // import service definition and implementation
    use example::Example;

    #[tokio::main]
    async fn main() {
        let addr = "127.0.0.1:23333";
        let ex = Arc::new(Example { });
        let server = Server::builder()
            .register(ex)
            .build()

        let listener = TcpListener::bind(addr).await.unwrap();
        server.accept(listener).await.unwrap();
    }

On the client side, let's call the `finite_loop` RPC function with a timeout of three seconds.

    use std::time::Duration;
    use tokio::time;
    use toy_rpc::client::{Client, Call};

    // assume the name of the crate is "example"
    // import service definitions and generated client stub functions
    use example::*;

    #[tokio::main]
    async fn main() {
        let client = Client::dial("127.0.0.1:23333").await
            .expect("Failed to connect to server");

        let call: Call<()> = client
            .set_next_timeout(Duration::from_secs(3))
            .example() // access `Example` service
            .finite_loop(()); // access `finite_loop` method
        let result = call.await; // This should give you `Err(Error::Timeout)`
    }

# TLS Support

Support for TLS is implemented with `rustls` and its `async` derivatives `async-rustls` and `tokio-rustls`.

An example using a self-signed certificate can be found in the GitHub repo.

More detailed documentations are underway.

## How to generate self-signed CA and certs (an example)

1. Create Root signing key: `openssl genrsa -out ca.key 4096`
2. Generate self-signed Root certificate: `openssl req -new -x509 -key ca.key -sha256 -subj "/C=CA/ST=BC/L=Vancouver/O=Example, Inc." -days 365 -out ca.cert`
3. Create a key for server: `openssl genrsa -out service.key 4096`
4. Create a signing request (CSR): `openssl req -new -key service.key -out service.csr -config certificate.conf` (see section below for details of `certificate.conf`)
5. Generate a certificate for the server: `openssl x509 -req -in service.csr -CA ca.cert -CAkey ca.key -CAcreateserial -out service.pem -days 365 -sha256 -extfile certificate.conf -extensions req_ext`

`certificate.conf`

    [req]
    default_bits = 4096
    prompt = no
    default_md = sha256
    req_extensions = req_ext
    distinguished_name = dn
    [dn]
    C = CA
    ST = BC
    L = Vancouver
    O = Example, Inc.
    CN = localhost
    [req_ext]
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = localhost
    IP.1 = ::1
    IP.2 = 127.0.0.1

# PubSub

A simple PubSub support is added in 0.8.0. A simple example can be found here.

A publisher can be created on the server side or the client side using the `publisher::<T: Topic>()` method, and a subscriber can be created using the `subscriber::<T: Topic>(cap: usize)` method. They both take one type parameter `T` which must implement the `toy_rpc::pubsub::Topic` trait. You can use the provided derive macro `#[derive(toy_rpc::macros::Topic)]` to define a struct as the pubsub message or by manually implementing the `toy_rpc::pubsub::Topic` trait on a type.

    use toy_rpc::macros::Topic;
    use serde::{Serializer, Deserialize};

    #[derive(Topic, Serialize, Deserialize)]
    pub struct Count(pub u32);

The message item type and topic name can also be customized using attribute `#[topic]`. For example

    #[derive(Serialize, Deserialize, Topic)
    #[topic(rename="C")] // This will only change topic name to "C", and the message item type is still `Count`
    pub struct Count(u32);

    #[derive(Topic)
    #[topic(item = "u32")] // This will only change the message item type
    pub struct Count { }

    #[derive(Topic)
    #[topic(rename = "C", item = "u32")] // Or customize both topic name and item type
    pub struct Count { }

Or manually implement the `Topic` trait

    #[derive(Serialize, Deserialize)]
    pub struct Count(pub u32);

    impl toy_rpc::pubsub::Topic for Count {
        type Item = Count; // The Item type must implement `Serialize` and `Deserialize`

        // A String identifier for the topic. The user must ensure it is unique
        fn topic() -> String {
            "Count"
        }
    }

A publisher can be created by specifying the topic in the type parameter.

    let publisher = client.publisher::<Count>(); // on client side
    // let publisher = server.publisher::<Count>(); // on server side

The `Publisher` implements the `futures::Sink<T>` trait where `T` is the type parameter representing the topic. In order to publish message to the topic, the `futures::SinkExt` trait must be imported.

    use futures::SinkExt;

    publisher.send(Count(7)).await.unwrap();

A subscriber can be created by specifying the topic in the type parameter and the capacity of its local buffer. Here we will create a subscriber on the client side listening to messages on the topic `Count` with a local capacity of 10.

    let subscriber = client.subscirber::<Count>(10).unwrap(); // on the client side
    // let subscriber = server.subscriber::<Count>(10).unwrap(); // on the server side (except for `actix-web`)

The `Subscriber` implements the `futures::Stream<Item = Result<T, toy_rpc::Error>>` trait where `T` is the type parameter representing the topic. In order to process incoming messages, the `futures::StreamExt` trait must be imported.

    use futures::StreamExt;

    if let Some(result) = subscriber.next().await {
        let item = result.unwrap(); // There could be errors recving incoming messages
        // do something with the item
    }

## Example

GitHub repo

## `Ack` for `Publish` message delivery

As of version 0.8.0-beta.0, `Ack` is added in the cases where explicit `Ack` is needed. `Ack` only applies to acknowledge receiving of `Publish` message and does NOT apply to any RPC requests/responses. There are three different `AckMode`

1. `AckModeNone`, which is the default mode for both the `Server` and `Client`. This mode is available on both the `Server` and the `Client` Under this mode, no `Ack` message will be required by the publisher or be sent by the subscriber.
2. `AckModeAuto`. This mode is available on both the `Server` and `Client`. Under this mode, both the server and the client will automatically reply with an `Ack` to any `Publish` message they receive.
3. `AckModeManual`. This mode is only available on `Client`. Under this mode, the subscriber needs to manually `.ack()` in order to get the published item. Please note that under the manual mode, the `Publisher` behaves the same as if it is under the `AckModeAuto` mode.

The behavior of publisher/subscriber will be discussed in different senarios below.

1. `Publisher` on the `Server` with `AckModeAuto`

   When a `Publisher` is created on the server side, the server's pubsub handler will wait for ALL `Ack`s from the subscribers, including that from `Subscriber` on the `Server`, in an asynchronous manner, meaning the publisher is able to continue publishing new messages even if some subscribers have not sent back `Ack` yet. Upon reaching the timeout, the server's pubsub handler will try to resend the same publish message (with the same sequence ID) to the `Subscriber`s that have not send back `Ack` messages. The server will stop retrying after the maximum number of retries is reached.

2. `Publisher` on the `Client` with `AckModeAuto` or `AckModeManual`

   When a `Publisher` is created on the client side, the client will wait for only ONE `Ack` message from the `Server` in an asynchronous manner, meaning the `Publisher` is able to continue publishing new messages even if the `Ack` message from the `Server` has not arrived. If the `Ack` message from the `Server` does not arrive before the timeout expires, the client will attempt to publish the same message (with the same message ID). The client (`Publisher`) will stop retrying after the maximum number of retries is reached.

   Once the `Publish` message is received by the `Server`, the message will be assigned a new sequence ID that is tracked only by the `Server`. The message will then be published to all subscribers under the topic, and the server will wait for ALL `Ack` messages from the subscribers in an asynchronous manner, meaning the server will be able to keep handling RPC requests or PubSub messages while waiting for `Ack` messages to come back. If not all `Ack` messages are sent back to the server before the timeout expires, the server will attempt to resend the same message with the same sequence ID number to the subscribers whose `Ack` messages are not received. The server will stop retrying after the maximum number of retries is reached.

3. `Subscriber` on the `Server` side with `AckModeAuto`

   Please note that the `Server` side does NOT support `AckModdManual`. Upon receiving a published message, the subscriber will automatically send back an `Ack` message to the PubSub handler on the server.

4. `Subscriber` on the `Client` side with `AckModeAuto`

   Upon receiving a published message, the `Client` will automatically send back an `Ack` message back to the `Server`.

5. `Subscriber` on the `Client` side with `AckModeManual`

   Instead of receiving the usual `Result<Topic::Item, Error>` from the `Subscriber` stream, the user will receive `Result<Delivery<Topic::Item>, Error>`. In order to get the usual `Topic::Item`, the user will need to call `.ack()` method on the `Delivery` object (ie. `let item = delivery.ack()`), which will send back an `Ack` message to the `Server`.

### How to use `AckMode`

By default, all `Server` and `Client` start with `AckModeNone`

    let server = Server::builder()
        .build(); // This will create a server with `AckModeNone`

    let client = Client::dial(ADDR)
        .await.unwrap(); // This will create a client with `AckModeNone`

`Ack` can be enabled by setting the `Server` or `Client` into the corresponding mode using the corresponding builder.

    let server = Server::builder() // This will start the builder with `AckModeNone`
        .set_ack_mode_auto() // This will set the ServerBuilder to `AckModeAuto`
        .build(); // This will build the server with `AckModeAuto`

    let client = Client::builder()
        .set_ack_mode_auto() // This will set the ClientBuilder to `AckModeAuto`
        .dial(ADDR)
        .await.unwrap(); // This will create a Client with `AckModeAuto`

    let client = Client::builder() // This will start the builder with `AckModeNone`
        .set_ack_mode_manual() // This will set the ClientBuilder to `AckModeManual`
        .dial(ADDR)
        .await.unwrap(); // This will create a Client with `AckModeManual`

The timeout and maximum number of retries for the publisher can also be configured

    let server = Server::builder()
        // Must enable Ack first
        .set_ack_mode_auto()
        // Sets how long the server will wait for Ack messages.
        //
        // This also affects the Publish messages sent by
        // `Publisher`s from the client side
        .set_publisher_retry_timeout(Duration::from_secs(5))
        // Sets many times the server will retry.
        //
        // This also affects the Publish messages sent by
        // `Publisher`s from the client side
        .set_publisher_max_num_retries(3)
        .build();

    let client = Client::builder()
        // Must set the builder into some `Ack` mode
        .set_ack_mode_auto()
        // Sets how long the client publisher will wait for Ack message from Server.
        //
        // This does ***NOT*** affect how long the server will wait for
        // `Ack` from the subscribers
        .set_publisher_retry_timeout(Duration::from_secs(5))
        // Sets how long the client publisher will retry to send the Publish
        // message to the server.
        //
        // This does ***NOT*** affect how many times the server will attempt to
        // resend the publish message to the subscribers
        .set_publisher_max_num_retries(3)
        .dial(ADDR)
        .await.unwrap();

# Examples

There are a few examples available on the GitHub repo. Here are some descriptions of those examples.

- "Raw" TCP server and client with `async-std` runtime
- "Raw" TCP server and client with `tokio` runtime
- Cancellation and timeout of RPC call
- Service definition, server implementation, and client implementation in three separate crates
  - service
  - server
  - client
- HTTP integrations
  - `actix-web`
  - `tide`
  - `warp`

# 0.9 Preview

## Objectives

The planned objectives for 0.9 can be found in this issue.

## Summary or the preview releases

More detailed information can be found in the corresponding page.

### 0.9.0-alpha.1

Relaxed method return type requirements.

### 0.9.0-alpha.2

Removed `AckMode`.

## 0.9.0-alpha.1

Relaxed method return type requirements.

Prior to this release, only `Result` type is accepted as the return type for exported methods. Now, for methods that do not really need to be a `Result` (eg. a simple addition), the return type no long needs to be wrapped inside a `Result`. A more detailed example is provided below and can be found in this example.

### Service definition

Please note that the codes that import the crates are intentionally omitted in the example below. Please refer to the example on github for more information.

#### Service definition with service struct impl

    #![allow(unused)]
    fn main() {
    pub struct Echo { }

    #[export_impl]
    impl Echo {
        /// This shows an exported method that returns a non-result type
        #[export_method]
        pub async fn echo_i32(&self, req: i32) -> i32 {
            req
        }

        /// This shows an exported method that returns a Result type
        #[export_method]
        pub async fn echo_if_equal_to_one(&self, req: i32) -> Result<i32, i32> {
            match req {
                1 => Ok(req),
                _ => Err(req), // This will present on the client call as `Error::ExecutionError`
            }
        }
    }

    }

#### Service definition with a trait

Please note that if `impl_for_client` is enabled, all the methods in the service trait must return a `Result` type.

    #![allow(unused)]
    fn main() {
    /// Here we define a trait `Arith` that comes with default implementations
    /// for all of its methods
    #[async_trait]
    #[export_trait]
    pub trait Arith {
        /// Addition
        #[export_method]
        async fn add(&self, args: (i32, i32)) -> i32 {
            args.0 + args.1
        }

        /// Subtraction
        #[export_method]
        async fn subtract(&self, args: (i32, i32)) -> i32 {
            args.0 - args.1
        }

        /// Multiplication
        #[export_method]
        async fn multiply(&self, args: (i32, i32)) -> i32 {
            args.0 * args.1
        }

        /// Division. We cannot divide by zero
        #[export_method]
        async fn divide(&self, args: (i32, i32)) -> Result<i32, String> {
            let (numerator, denominator) = args;
            match denominator {
                0 => return Err("Divide by zero!".to_string()),
                _ => Ok( numerator / denominator )
            }
        }
    }

    /// Here we are going to define another trait (service) tha is almost
    /// identical to `Arith` shown above, but we are going to generate the trait
    /// impl for the client using `impl_for_client` argument in our `#[export_trait]`
    /// attribute. Plus, we will not going to supply a default implementation either.
    #[async_trait]
    /// All methods must be exported if client trait impl generation is enabled.
    /// If `impl_for_client` is enabled, all methods in the trait must return
    /// a Result
    #[export_trait(impl_for_client)]
    pub trait Arith2 {
        #[export_method]
        async fn add(&self, args: (i32, i32)) -> anyhow::Result<i32>;

        #[export_method]
        async fn subtract(&self, args: (i32, i32)) -> anyhow::Result<i32>;

        #[export_method]
        async fn multiply(&self, args: (i32, i32)) -> anyhow::Result<i32>;

        #[export_method]
        async fn divide(&self, args: (i32, i32)) -> anyhow::Result<i32>;
    }
    }

### Server implementation

Nothing really changed in terms of usage for the server side. The server side example code is attached below for completeness.

    struct Abacus { }

    /// We will simply use the default implementation provided in the trait
    /// definition for all except for add
    #[async_trait]
    #[export_trait_impl]
    impl Arith for Abacus {
        /// We are overriding the default implementation just for
        /// the sake of demo
        async fn add(&self, args: (i32, i32)) -> i32 {
            args.0 + args.1
        }
    }

    /// For now, you need a separate type for a new service
    struct Abacus2 { }

    #[async_trait]
    #[export_trait_impl]
    impl Arith2 for Abacus2 {
        async fn add(&self, args: (i32, i32)) -> anyhow::Result<i32> {
            Ok(args.0 + args.1)
        }

        async fn subtract(&self, args: (i32, i32)) -> anyhow::Result<i32> {
            Ok(args.0 - args.1)
        }

        async fn multiply(&self, args: (i32, i32)) -> anyhow::Result<i32> {
            Ok(args.0 * args.1)
        }

        async fn divide(&self, args: (i32, i32)) -> anyhow::Result<i32> {
            let (numerator, denominator) = args;
            match denominator {
                0 => return Err(anyhow::anyhow!("Divide by zero!")),
                _ => Ok( numerator / denominator )
            }
        }
    }

    #[tokio::main]
    async fn main() {
        env_logger::init();

        let addr = "127.0.0.1:23333";
        let echo_service = Arc::new(
            Echo { }
        );
        let arith = Arc::new(Abacus { });
        let arith2 = Arc::new(Abacus2 { });

        let server = Server::builder()
            .register(echo_service)
            .register(arith)
            .register(arith2)
            .build();

        let listener = TcpListener::bind(addr).await.unwrap();

        log::info!("Starting server at {}", &addr);

        let handle = task::spawn(async move {
            server.accept(listener).await.unwrap();
        });
        handle.await.expect("Error");
    }

### Client side implementation

Like the server side implementation, there isn't much change to the client side usage. The only thing worth mentioning is that even if the method doesn't return a `Result` in the service definition, the client will still get a `Result` because there could be errors with connection or serialization/deserialization.

    #[tokio::main]
    async fn main() {
        let _ = run().await;
    }

    async fn run() -> anyhow::Result<()> {
        env_logger::init();

        // Establish connection
        let addr = "127.0.0.1:23333";
        let client = Client::dial(addr).await.unwrap();

        // Perform RPC using `call()` method
        let call: Call<i32> = client.call("Echo.echo_i32", 13i32);
        let reply = call.await?;
        println!("{:?}", reply);

        let reply: i32 = client.call("Echo.echo_i32", 1313i32).await?;
        println!("{:?}", reply);

        let ok_result = client
            .echo() // refering to `Echo` service
            .echo_if_equal_to_one(1) // refering to `echo_if_equal_to_one` method
            .await;
        let err_result = client
            .echo()
            .echo_if_equal_to_one(2)
            .await;
        println!("Ok result: {:?}", ok_result);
        println!("Err result: {:?}", err_result);

        // Demo usage with the `Arith` trait
        let addition = client
            .arith() // generated for `Arith` service
            .add((1, 3)).await; // call `add` method
        println!("{:?}", addition);

        // Although the return type of `divide` is a `Result<T, E>`,
        // the execution result will be mapped to `Result<T, toy_rpc::Error>`
        // where `E` is mapped to `toy_rpc::Error::ExecutionError` so that
        //   (1) the Error type doesn't need to implement `Serialize` and
        //   (2) the users don't need to unwrap twice
        let division = client
            .arith()
            .divide((3, 1)).await;
        println!("{:?}", division);

        // let's try to get an execution error
        let divide_by_zero = client
            .arith()
            .divide((3, 0)).await;
        println!("{:?}", divide_by_zero);

        // Now let's take a look at using the generated trait impl for the client.
        let addition = Arith2::add(&client, (7, 8)).await;
        println!("{:?}", addition);

        let division = Arith2::divide(&client, (7, 2)).await;
        println!("{:?}", division);
        let divide_by_zero = Arith2::divide(&client, (7, 0)).await;
        println!("{:?}", divide_by_zero);

        client.close().await;
        Ok(())
    }

# Removing `AckMode`

The `Ack` implementation wasn't particular useful under practical situations as the client would likely reconnect with a different port and would thus be treated as a new client.

As the long term goal is to switch to some protocol like AMQP (which is another project that I am currently working on), proper support for message delivery acknowledgement can be expected when that is ready.

# Change Log

## 0.8.4

- Unified all connection related error (read/write) to `Error::IoError(_)`

## 0.8.3

- Reverting back to 2018 edition

## 0.8.2

- Fixed a bug where attribute macros doesn't parse trait path and type path correctly

## 0.8.1

- Updated dependencies and corresponding examples
  - `axum` to the latest version
  - WebSocket dependencies like `tungstenite`, etc
  - TLS dependencies like `rustls`, etc.
- Switched from `async-rustls` to `futures-rustls`
- The error message of sending on a closed channel upon ending a client or server becomes a debug message now.

## 0.8.0

- Finalized features

## 0.8.0-beta

- Added AckMode impl
- Added attribute #[topic()] for derive macro #[derive(Topic)]
- Added integration with `axum`

## 0.8.0-alpha

### Breaking Changes

- Communication protocol is changed

### Non-breaking Changes

- Added pubsub support
- Added RPC trait implementation generation for client

## 0.7.4

- Fixed wrong documentation for `Client::with_stream<T>(stream: T)`

## 0.7.3 (yanked)

## 0.7.2

- Relaxed trait bounds on generic type `T` in `Client::with_stream<T>(stream: T)`

## 0.7.1

### Bug fix

- Fixed a bug where large payload is not written entirely with `tokio` runtime

## 0.7.0

### Breaking Changes

- The blocking RPC call on the client side is renamed to `call_blocking`
- The asynchronous RPC call on the client side is renamed to `call`
- The `call` method returns a `Call<Res>` type where `Res` represents the `Ok` type of result. The request is sent by a background task and thus the new `call` method is similar to the old `spawn_task` in terms of usage.

### New Features

- Cancellation. The `Call<Res>` type returned by the `call` method can be canceled by using the `cancel()` method.
- Timeout. A timeout can be set for the next request by calling `client.timeout(duration)`. only one request after setting the timeout is going to run with a timeout. If you want to set timeout for multiple requests, you need to set the timeout for each of them.

## 0.6.1

- Multiple objects of the same types can be registered on the same server again, but you will need to use the `ServerBuilder::register_with_name` method as opposed to the regular `ServerBuilder::register`. More details can be found in `ServerBuilder::register_with_name`'s documentation.

## 0.6.0

### Breaking Changes

- In short, this update makes the crate resemble closer to the usage of `go`'s `net/rpc` package
- Service registration is simplified to `Server::builder().register(foo_service).build()`. The examples will be updated accordingly. Thus
  - `service!()` macro will be deprecated
  - `register` function now takes only one argument, which is the instance of the service
  - on the client side, the service name will just be the name of the struct. for example, to call a RPC method on `struct Foo { }` service, the client simply uses `.async_call("Foo.<method>").await` where `<method>` should be replaced with the RPC method
  - you can still register multiple services on the same server. However, only one object of the same type can be registered on the same server. Multiple servers are needed to have multiple objects of the same type.
- Re-defined the custom `Error` type

### Non-breaking changes

- Fixed bug where client does not interpret error message correctly
- Fixed bug with `accept_websocket` crashes with incorrect protocol

## 0.5.4

- Handlers are now stored as a `fn` pointer as opposed to a trait object.

## 0.5.3

- The `#[export_impl]` macro now generates client stub functions by generating a new trait for `toy_rpc::Client`.

## 0.5.0

Breaking changes

- HTTP integration is now accomplished using WebSocket with `async_tungstenite`, and thus HTTP connections of versions <0.5.0 are not compatible with versions >=0.5.0.
- The custom binary transport protocol now includes a magic byte at the beginning, making versions <0.5.0 NOT compatible with versions >= 0.5.0;
- `toy_rpc::error::Error` changed from struct-like variants to simple enum variants
- Changes to feature flags
  - "logging" feature flag is removed
  - "surf" feature flag is removed
  - "tide" is changed to "http_tide"
  - "actix-web" is changed to "http_actix_web"
  - added "http_warp" feature flag
  - added "async_std_runtime"
  - added "tokio_runtime"

Non-breaking changes

- Removed `Stream` and `Sink` impl from the custom binary transport protocol `Frame`

## 0.4.5

- Added `Sink` implementation for the custom binary transport protocol `Frame`

## 0.4.4

- Modified traits `CodecRead`, `CodecWrite`, `ServerCodec`, `ClientCodec` to no longer return number of bytes written
- The number of bytes written for header and body will be logged separately

## 0.4.3

- Removed previously unused NoneError
- Unified `call`, `async_call` and `spawn_task` for socket client and HTTP client. The `call_http`, `async_call_http`, and `spawn_task_http` methods are kept for compatibility.

## 0.4.2

- Temporary fix of `spawn_task()` and `spawn_task_http()` with `Arc<Mutex<_>>` until lifetime with async task is figured out. As a result, `Client` no longer needs to be declared `mut`.

## 0.4.1

- Updated documentation

## 0.4.0

- Added `actix-web` feature flag to support integration with `actix-web`

## 0.3.1

- Added `serde_rmp` features flag
- Updated and corrected examples in the documentation

## 0.3.0

- Added `serde_cbor` feature flag
- Changed `bincode` feature flag to `serde_bincode`