Significantly update the documentation
This commit is contained in:
162
src/lib.rs
162
src/lib.rs
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Eagle - A library for easy communication in full-stack Rust applications
|
||||
Eagle - A simple library for RPC in Rust
|
||||
Copyright (c) 2024 KodiCraft
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@@ -15,6 +15,139 @@ GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! # Eagle - A simple library for RPC in Rust
|
||||
//!
|
||||
//! <div class="warning">Eagle is still in early development. This documentation is subject to change and may not be entirely accurate.</div>
|
||||
//!
|
||||
//! Eagle is a library for building RPC protocols in Rust. It uses a macro
|
||||
//! to transform your protocol definition into the necessary code to allow
|
||||
//! communication between a server and a client.
|
||||
//!
|
||||
//! Eagle uses [`tokio`](https://tokio.rs) as its async runtime and
|
||||
//! [`ron`](https://crates.io/crates/ron) for serialization.
|
||||
//!
|
||||
//! ## Usage
|
||||
//! `eagle` is designed to be used to create your own protocol crate. We
|
||||
//! recommend creating a new cargo workspace for your project with a shared
|
||||
//! crate which will contain your protocol definition and individual crates
|
||||
//! for the server and client.
|
||||
//!
|
||||
//! In your shared crate, you can define your protocol using the [`Protocol`]
|
||||
//! derive macro. This will generate the necessary code for the server and
|
||||
//! client to communicate.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use eagle::Protocol;
|
||||
//!
|
||||
//! #[derive(Protocol)]
|
||||
//! pub enum Example {
|
||||
//! Add((i32, i32), i32),
|
||||
//! Length(String, usize),
|
||||
//! /* ... */
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The [`Protocol`] derive macro will generate all the necessary code, including
|
||||
//! your handler trait, your server struct, and your client struct.
|
||||
//!
|
||||
//! On your server, you will need to implement the handler trait. This trait
|
||||
//! describes the functions that the client can request from the server.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use eagle::Protocol;
|
||||
//! # #[derive(Protocol)]
|
||||
//! # pub enum Example {
|
||||
//! # Add((i32, i32), i32),
|
||||
//! # Length(String, usize),
|
||||
//! # /* ... */
|
||||
//! # }
|
||||
//! #
|
||||
//! #[derive(Clone)]
|
||||
//! pub struct Handler;
|
||||
//! impl ExampleServerHandler for Handler {
|
||||
//! async fn add(&mut self, a: i32, b: i32) -> i32 {
|
||||
//! a + b
|
||||
//! }
|
||||
//! async fn length(&mut self, s: String) -> usize {
|
||||
//! s.len()
|
||||
//! }
|
||||
//! /* ... */
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! To start the server, you simply need to use the generated server struct and
|
||||
//! pass it your handler.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use eagle::Protocol;
|
||||
//! # #[derive(Protocol)]
|
||||
//! # pub enum Example {
|
||||
//! # Add((i32, i32), i32),
|
||||
//! # Length(String, usize),
|
||||
//! # /* ... */
|
||||
//! # }
|
||||
//! #
|
||||
//! # #[derive(Clone)]
|
||||
//! # pub struct Handler;
|
||||
//! # impl ExampleServerHandler for Handler {
|
||||
//! # async fn add(&mut self, a: i32, b: i32) -> i32 {
|
||||
//! # a + b
|
||||
//! # }
|
||||
//! # async fn length(&mut self, s: String) -> usize {
|
||||
//! # s.len()
|
||||
//! # }
|
||||
//! # }
|
||||
//! #
|
||||
//! # tokio_test::block_on(async {
|
||||
//! let handler = Handler;
|
||||
//! let address = "127.0.0.1:12345"; // Or, if using the 'unix' feature, "/tmp/eagle.sock"
|
||||
//! let server_task = tokio::spawn(ExampleServer::bind(handler, address));
|
||||
//! # });
|
||||
//! ```
|
||||
//!
|
||||
//! Please note the usage of `tokio::spawn`. This is because the `bind` function
|
||||
//! will not return until the server is closed. You can use the `abort` method
|
||||
//! on the task to close the server.
|
||||
//!
|
||||
//! On the client side, you can simply use the generated client struct to connect
|
||||
//! to the server and begin sending queries.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use eagle::Protocol;
|
||||
//! # #[derive(Protocol)]
|
||||
//! # pub enum Example {
|
||||
//! # Add((i32, i32), i32),
|
||||
//! # Length(String, usize),
|
||||
//! # /* ... */
|
||||
//! # }
|
||||
//! #
|
||||
//! # #[derive(Clone)]
|
||||
//! # pub struct Handler;
|
||||
//! # impl ExampleServerHandler for Handler {
|
||||
//! # async fn add(&mut self, a: i32, b: i32) -> i32 {
|
||||
//! # a + b
|
||||
//! # }
|
||||
//! # async fn length(&mut self, s: String) -> usize {
|
||||
//! # s.len()
|
||||
//! # }
|
||||
//! # }
|
||||
//! #
|
||||
//! # tokio_test::block_on(async {
|
||||
//! # let handler = Handler;
|
||||
//! let address = "127.0.0.1:12345"; // Or, if using the 'unix' feature, "/tmp/eagle.sock"
|
||||
//! # let server_task = tokio::spawn(ExampleServer::bind(handler, address));
|
||||
//! let client = ExampleClient::connect(address).await.unwrap();
|
||||
//! # // Wait for the server to start, the developer is responsible for this in production
|
||||
//! # tokio::time::sleep(std::time::Duration::from_millis(10)).await;
|
||||
//! assert_eq!(client.add(2, 5).await.unwrap(), 7);
|
||||
//! # });
|
||||
//! ```
|
||||
//!
|
||||
//! The client can be closed by calling the `close` method on the client struct.
|
||||
//! This will abort the connection.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse2, spanned::Spanned, DeriveInput, Field, Ident};
|
||||
@@ -26,6 +159,33 @@ compile_error!("You must enable either the 'tcp' or 'unix' feature");
|
||||
#[cfg(all(feature = "unix", not(unix)))]
|
||||
compile_error!("The 'unix' feature requires compiling for a unix target");
|
||||
|
||||
/// Generate all the necessary RPC code for a protocol from an enum describing it.
|
||||
///
|
||||
/// This macro will generate various enums and structs to enable communication
|
||||
/// between a server and a client. The following items will be generated, where {}
|
||||
/// is the name of the protocol enum:
|
||||
/// - `{}ServerHandler` - A trait that the server must implement to handle queries
|
||||
/// - `{}Server` - A struct that the server uses to communicate with clients
|
||||
/// - `{}Client` - A struct that the client uses to communicate with a server
|
||||
///
|
||||
/// Each variant of the passed enum represents a query that the client can send to the
|
||||
/// server. The first field of each variant is the question (serverbound), the second field
|
||||
/// is the answer (clientbound). You may use tuples to represent sending multiple arguments and
|
||||
/// you may use the unit type `()` to represent no arguments. Only data types which implement
|
||||
/// [`Clone`], [`serde::Serialize`], and [`serde::Deserialize`] can be used.
|
||||
///
|
||||
/// For more information on how to use the generated code, see the [crate-level documentation](index.html).
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use eagle::Protocol;
|
||||
///
|
||||
/// #[derive(Protocol)]
|
||||
/// pub enum Example {
|
||||
/// Add((i32, i32), i32),
|
||||
/// Length(String, usize),
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(Protocol)]
|
||||
pub fn derive_protocol_derive(input: TokenStream) -> TokenStream {
|
||||
let expanded = derive_protocol(input.into());
|
||||
|
||||
Reference in New Issue
Block a user