occams-rpc 0.3.0

razor-rpc is a modular, pluggable RPC for high throughput scenario, supports various runtimes, with a low-level streaming interface, and high-level remote API call interface.
Documentation
# The API interface

A Service is defined by user, should be:
- called with immutable &self, because on the server-side, we want it to have Sync
- we should make client-side be able use the same trait, to leverage static check of rust compiler.
- Although the service handler will not return RpcIntErr, but the client-side might get a RpcIntErr from connection,
 the service method should return Result<(), RpcError<E>>, Where E is the user defined error which implements RpcErrCodec.
- All the method in the trait should be `async fn` or `impl Future`, optionally wrapped by async_trait. (Because the client-side is only async)
- Try to compatible with GRPC definition

In each Request, contains `service` field, which should mach service name (in PascalCase), `method` field, mach the trait method name (in lower_case). (This rule is the same with GRPC)

# Service trait

We suggest you define service interface first, in separate code/crate shared between server and client.
```
#[endpoint_async(DemoClient)]
pub trait DemoService {
    async fn foo() -> Result<(), RpcError<MyError>>;
}
```

Because rust does not allow macro to read trait defined in external crate,
you have to apply #[endpoint_async] macro to generate the client code with specified struct name, which implements the service trait.

The generated `DemoClient` is a wrapper to

will have a generic param `<C: ClientCaller<Factory: ClientFact<Task = APIClientReq>>>`, and a new function to wrap a generic ClientCaller.

# Client

At the client-side. there should be:

- ClientStream: from razor-stream::client_impl::RpcClient.
- ClientCaller: from razor_stream
    - ClientPool: To maintain worker pool,
    - FailoverPool: loadbalance and fail over then server unreachable.
- ClientFactsDefault: default facts that implements razor-stream::client::ClientFact
- AsyncEndpoint: provides `async fn call`  (razor_rpc::client), a wrapper of ClientCaller. AsyncEndpoint can be clone if ClientCaller impl Clone.
- BlockingEndpoint: provides `fn call` (razor_rpc::client)

# Server

There's a Service trait which defines a `serve` function, which accept request

```rust
struct Request<C: Codec> {
    seq: u64,
    service: String,
    method: String,
    req: Option<Vec<u8>>,
    codec: Arc<C>,
    noti: RespNoti<Response>,
}
```

There's two types of Service trait:
- ServiceStatic: For static distribution. Impl generated by macros. Note that even ServiceMuxDyn
  has impl ServiceStatic.
- ServiceDyn: For dynamic distribution, can register the ServiceDyn trait object into ServiceMuxDyn.

The difference is only in serve() function, the `ServiceStatic::serve()` is using `impl Future`, while the
`ServiceDyn::serve_dyn` is wrapped with async_trait.

There's blanket trait to impl ServiceDyn on all ServiceStatic object, so the user does not need to
care about it.

The RpcServer dispatcher will use ServiceStatic, since every service component have impl
ServiceStatic.

The function `serve(req)` in a Service trait should:
    - decode the request from bytes to request struct
    - call the method in itself, to get a response
    - set_result or set_error to the Request, and encode a Response contains message bytes or an error
    - send the Response through RespNoti

## macros

### service `#[service]`

The `#[service]` macro is applied to an `impl` block to automatically generate the `ServiceStatic` implementation for the type.

-   When applied to an inherent `impl` block, methods intended as service methods should be marked with `#[method]`.
-   When applied to a trait `impl` block, all methods defined in the trait will be registered as service methods (no `#[method]` marker needed).
-   The macro can handle methods with different user-defined error types (e.g. `String`, `i32`, `nix::errno::Errno`) in the same `impl` block.

The service method recognizes:
- `async fn`
- `impl Future`
- trait methods wrapped by `async_trait`

The macro will iterate all methods and generate a `ServiceStatic::serve()` implementation.

#### Example: Inherent `impl`

```rust
use razor_rpc_api_macros::{method, service};
use razor_rpc_core::error::RpcError;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
pub struct MyArg {
    pub value: u32,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct MyResp {
    pub result: u32,
}

#[derive(Clone)]
pub struct MyServiceInherentImpl;

#[service]
impl MyServiceInherentImpl {
    #[method]
    async fn mul(&self, arg: MyArg) -> Result<MyResp, RpcError<String>> {
        Ok(MyResp { result: arg.value * 2 })
    }

    #[method]
    async fn div(&self, arg: MyArg) -> Result<MyResp, RpcError<i32>> {
        if arg.value == 0 {
            return Err(1.into()); // Return an i32 error
        }
        Ok(MyResp { result: arg.value / 2 })
    }
}
```

#### Example: Trait `impl`

```rust
use razor_rpc_api_macros::service;
use razor_rpc_core::error::RpcError;
use serde::{Deserialize, Serialize};
use std::future::Future;

// Assuming MyArg and MyResp are defined as above

pub trait MyService {
    fn add(&self, arg: MyArg) -> impl Future<Output = Result<MyResp, RpcError<String>>> + Send;
    fn sub(&self, arg: MyArg) -> impl Future<Output = Result<MyResp, RpcError<String>>> + Send;
}

#[derive(Clone)]
pub struct MyServiceImpl;

#[service]
impl MyService for MyServiceImpl {
    async fn add(&self, arg: MyArg) -> Result<MyResp, RpcError<String>> {
        Ok(MyResp { result: arg.value + 10 })
    }

    async fn sub(&self, arg: MyArg) -> Result<MyResp, RpcError<String>> {
        Ok(MyResp { result: arg.value - 10 })
    }
}
```

### Service Dispatcher `#[service_mux_struct]`

The `#[service_mux_struct]` macro is applied to a **struct** to implement `ServiceStatic` on it. It acts as a dispatcher, routing `serve()` calls to the correct service based on the `req.service` field.

Each field in the struct must hold a service that implements `ServiceStatic` (e.g., wrapped in an `Arc`). The macro generates a `serve` implementation that matches `req.service` against the field names of the struct.

#### Example: Service Dispatcher Struct

```rust
use razor_rpc_api_macros::{service, service_mux_struct, method};
use razor_rpc_core::error::RpcError;
use serde::{Deserialize, Serialize};
use std::sync::Arc;

// Define some request/response types
#[derive(Debug, Deserialize, Serialize)]
pub struct MyArg { pub value: u32, }
#[derive(Debug, Deserialize, Serialize)]
pub struct MyResp { pub result: u32, }

// Define a service
pub struct MyFirstService;
#[service]
impl MyFirstService {
    #[method]
    async fn add(&self, arg: MyArg) -> Result<MyResp, RpcError<String>> {
        Ok(MyResp { result: arg.value + 1 })
    }
}

// Define another service
pub struct MySecondService;
#[service]
impl MySecondService {
    #[method]
    async fn sub(&self, arg: MyArg) -> Result<MyResp, RpcError<String>> {
        Ok(MyResp { result: arg.value - 1 })
    }
}

// Use the dispatcher to combine them
#[service_mux_struct]
pub struct MyServiceDispatcher {
    my_first: Arc<MyFirstService>,
    my_second: Arc<MySecondService>,
}

// The generated `serve` will look something like this:
//
// impl<C: Codec> ServiceStatic<C> for MyServiceDispatcher {
//     async fn serve(&self, req: Request<C>) -> ... {
//         match req.service.as_str() {
//             "my_first" => self.my_first.serve(req).await,
//             "my_second" => self.my_second.serve(req).await,
//             _ => req.set_rpc_error(RpcIntErr::Service),
//         }
//     }
// }
`