An open API service indexing awesome lists of open source software.

https://github.com/yamadapc/actix-handler-macro

Procedural macros for Actix Handler trait impl generation.
https://github.com/yamadapc/actix-handler-macro

actix rust

Last synced: 10 days ago
JSON representation

Procedural macros for Actix Handler trait impl generation.

Awesome Lists containing this project

README

        

# actix_handler_macro
![Rust](https://github.com/yamadapc/actix-handler-macro/workflows/Rust/badge.svg)
[![Docs](https://docs.rs/actix-handler-macro/badge.svg)](https://docs.rs/actix-handler-macro)
- - -

Helper macros for using Actix.

```rust
use actix::Message;
use actix_handler_macro::{actix_handler, Actor};

#[derive(Actor)]
struct Example;
type ExampleContext = actix::Context;

#[derive(Message)]
#[rtype(result = "String")]
struct Greeting {
name: String,
}

#[actix_handler]
impl Example {
fn greet(&self, message: Greeting, _ctx: &ExampleContext) -> String {
format!("Hello {}", message.name).to_string()
}
}
```
actor_derive;
expand_addr;
expand_impl_handlers;
expand_method_handlers;
options;
utils;

expand_impl_handlers::expand_item_impl;
options::{parse_options, Options};
proc_macro::TokenStream;
quote::quote;
syn::{parse_macro_input, AttributeArgs, DeriveInput, Item};
utils::compilation_error;

Allows writing Actix actors with impl blocks.

## Motivation
Actix can be quite verbose when declaring handlers:

```rust
use actix::Actor;
use actix::Context;
use actix::Handler;
use actix::Message;

struct Example;

impl Actor for Example {
type Context = Context;
}

#[derive(Message)]
#[rtype(result = "String")]
struct Greeting { name: String }

impl Handler for Example {
type Result = String;

fn handle(&mut self, msg: Greeting, ctx: &mut Context) -> Self::Result {
unimplemented!()
}
}
```

`actix_derive_macro` reads an `impl` block and generates a bit of this code for each method.

## Usage
```rust
use actix_handler_macro::{Actor, actix_handler};
use actix::{Actor, Addr, Context, Message, System};

#[derive(Actor)]
struct Example;

type ExampleContext = Context;

#[derive(Clone, Message)]
#[rtype(result = "String")]
struct Greeting { name: String }

#[actix_handler]
impl Example {
fn greet(&self, message: Greeting, _ctx: &ExampleContext) -> String {
format!("Hello {}", message.name).to_string()
}
}

fn example_usage() {
let mut sys = System::new("actix-test-runtime");
let addr: Addr = Example {}.start();

sys.block_on(async move {
let greeting = Greeting { name: "you".to_string() };
// `Example` has its handler impl block
let result = addr.send(greeting.clone()).await.ok().unwrap();
assert_eq!(result, "Hello you");

// An Addr trait is also expanded:
let result = addr.greet(greeting.clone()).await.ok().unwrap();
assert_eq!(result, "Hello you")
});
}
```

This will expand a `Handler` impl for each method in Example.

## Actor `...Addr` trait
It'll also output a trait `GreetingAddr` and its implementation for `Addr` with
convenience methods:

```ignore
// Example output
trait GreetingAddr {
fn greet(self: &Self, msg: Greeting) -> actix::prelude::Request;
}
```

## RecipientRequest

Optionally, the trait can use a `actix::Recipient` and return a `actix::RecipientRequest`.

```rust
use actix::{Message};
use actix_handler_macro::{actix_handler, Actor};

#[derive(Actor)]
struct Example;
type ExampleContext = actix::Context;
#[derive(Message)]
#[rtype(result = "String")]
struct Greeting { name: String }

#[actix_handler(use_recipient)]
impl Example {
fn greet(&self, message: Greeting, _ctx: &ExampleContext) -> String {
format!("Hello {}", message.name).to_string()
}
}
```

`#[actix_handler(use_recipient)]` will expand the `GreetingAddr` trait as:

```skip
// Example output
trait GreetingAddr {
fn greet(self: &Self, msg: Greeting) -> actix::RecipientRequest;
}
```

A mock of the actor could be implemented as:
```skip
use actix::Message;
use actix_handler_macro::Actor;

#[derive(Actor)]
struct Example;

#[derive(Message)]
#[rtype(result = "String")]
struct Greeting;

#[derive(Actor)]
struct ExampleMock {
mocker: actix::Addr>,
}

impl GreetingAddr for ExampleMock {
fn greet(self: &Self, msg: Greeting) -> actix::prelude::RecipientRequest {
self.mocker.clone().recipient().send(msg)
}
}
```