prost_simple_rpc/lib.rs
1//! A simple RPC library to be used together with `prost` for defining type-safe RPC services.
2//!
3//! This library lets you generate traits for implementing a generic RPC mechanism using Protobuf as
4//! the schema language. You have to supply your own underlying transport mechanism, for example
5//! WebSockets, UNIX pipes, HTTP, etc.
6//!
7//! You probably want to use this library together with `prost-simple-rpc-build` to generate the
8//! code for all of the traits defined in this crate.
9//!
10//! # Usage
11//!
12//! Start by defining a schema for your service in e.g. `src/schema/echo/service.proto`:
13//!
14//! ```proto
15//! syntax = "proto3";
16//!
17//! package echo;
18//!
19//! // The Echo service. This service returns back the same data that it is given.
20//! service Echo {
21//! // Echoes back the data sent, unmodified.
22//! rpc Echo (EchoRequest) returns (EchoResponse);
23//! }
24//!
25//! // The request for an `Echo.Echo` call.
26//! message EchoRequest {
27//! // The data to be echoed back.
28//! bytes data = 1;
29//! }
30//!
31//! // The response for an `Echo.Echo` call.
32//! message EchoResponse {
33//! // The echoed back data from `EchoRequest.data`.
34//! bytes data = 1;
35//! }
36//! ```
37//!
38//! Use `prost`, `prost-build` and `prost-simple-rpc-build` to generate Rust code for this service, by
39//! putting this in your `build.rs`:
40//!
41//! ```rust,ignore
42//! extern crate prost_build;
43//! extern crate prost_simple_rpc_build;
44//!
45//! fn main() {
46//! prost_build::Config::new()
47//! .service_generator(Box::new(prost_simple_rpc_build::ServiceGenerator::new()))
48//! .compile_protos(
49//! &["src/schema/echo/service.proto"],
50//! &["src/schema"],
51//! )
52//! .unwrap();
53//! }
54//! ```
55//!
56//! Then, include the generated code in your Rust build, for example in `main.rs`. There are a bunch of
57//! extra crate dependencies for the generated code:
58//!
59//! ```rust,ignore
60//! extern crate bytes;
61//! extern crate failure;
62//! extern crate futures;
63//! extern crate prost;
64//! #[macro_use]
65//! extern crate prost_derive;
66//! extern crate prost_simple_rpc;
67//! extern crate tokio;
68//!
69//! mod schema {
70//! pub mod echo {
71//! include!(concat!(env!("OUT_DIR"), "/echo.rs"));
72//! }
73//! }
74//!
75//! fn main() {
76//! // ...
77//! }
78//! ```
79//!
80//! ## Client
81//!
82//! Let's say you want to create a client for your service. You need to implement a `Handler` that
83//! handles the transport for your client calls. Let's imagine you have some form of `WebSockets`
84//! transport:
85//!
86//! ```rust,ignore
87//! struct WebSocketTransport { /* ... */ }
88//!
89//! impl prost_simple_rpc::handler::Handler for WebSocketTransport {
90//! // From our imaginary websocket library:
91//! type Error = websocket::Error;
92//! // This type is generated by prost-simple-rpc:
93//! type Descriptor = schema::echo::EchoDescriptor;
94//! // From our imaginary websocket library:
95//! type CallFuture = websocket::Future;
96//!
97//! /// Perform a raw call to the specified service and method.
98//! fn call(
99//! &mut self,
100//! method: <Self::Descriptor as descriptor::ServiceDescriptor>::Method,
101//! input: bytes::Bytes,
102//! ) -> Self::CallFuture {
103//! // You can use information from the descriptors to include in the request:
104//! self.websocket.call(Self::Descriptor::name(), method.name(), input)
105//! }
106//! }
107//! ```
108//!
109//! You can now use this handler with the client generated by `prost-simple-rpc`:
110//!
111//! ```rust,ignore
112//! fn main() {
113//! let websocket = WebSocketTransport::connect("...");
114//! let client = schema::echo::EchoClient::new(websocket);
115//! let future = client.echo(schema::echo::EchoRequest { /* ... */ });
116//! // ... use the future to wait for a response.
117//! }
118//! ```
119//!
120//! ## Server
121//!
122//! To create a server for your service, start by implementing the generated service trait for the
123//! service:
124//!
125//! ```rust,ignore
126//! struct EchoService;
127//!
128//! #[derive(Debug, Eq, Fail, PartialEq)]
129//! #[fail(display = "Error!")]
130//! struct Error;
131//!
132//! impl schema::echo::Echo for EchoService {
133//! // You can supply an error type here if your service can fail.
134//! type Error = Error;
135//! // The future type used in the `echo()` method; you can of course use Box<Future<...>> here
136//! // but this library assumes unboxed futures by default.
137//! type EchoFuture = futures::future::FutureResult<schema::echo::EchoResponse, Self::Error>;
138//!
139//! fn echo(&self, input: schema::echo::EchoRequest) -> Self::EchoFuture {
140//! futures::future::ok(schema::echo::EchoResponse { data: input.data })
141//! }
142//! }
143//! ```
144//!
145//! You can now wrap this service with the generated server implementation to get something that can be
146//! plugged into your preferred routing system:
147//!
148//! ```rust,ignore
149//! fn main() {
150//! let server = schema::echo::EchoServer::new(EchoService);
151//!
152//! websocket::spawn_server(move |request| {
153//! // You would probably normally look up the right method descriptor via some kind of routing
154//! // information; here's a hard-coded example:
155//! let method = schema::echo::EchoMethodDescriptor::Echo;
156//!
157//! server.call(method, request.data);
158//! });
159//! }
160//! ```
161//!
162#![deny(missing_docs)]
163#![deny(missing_debug_implementations)]
164#![deny(missing_copy_implementations)]
165#![deny(trivial_casts)]
166#![deny(trivial_numeric_casts)]
167#![deny(unsafe_code)]
168#![deny(unstable_features)]
169#![deny(unused_import_braces)]
170#![deny(unused_qualifications)]
171#![cfg_attr(feature = "dev", allow(unstable_features))]
172#![cfg_attr(feature = "dev", feature(plugin))]
173#![cfg_attr(feature = "dev", plugin(clippy))]
174
175extern crate bytes;
176extern crate failure;
177#[macro_use]
178extern crate failure_derive;
179extern crate futures;
180extern crate prost;
181
182#[doc(hidden)]
183pub mod __rt;
184pub mod descriptor;
185pub mod error;
186pub mod handler;