# Actori [![Build Status](https://travis-ci.org/actori/actori.svg?branch=master)](https://travis-ci.org/actori/actori) [![Build status](https://ci.appveyor.com/api/projects/status/aytxo1w6a88x2cxk/branch/master?svg=true)](https://ci.appveyor.com/project/fafhrd91/actori-n9e64/branch/master) [![codecov](https://codecov.io/gh/actori/actori/branch/master/graph/badge.svg)](https://codecov.io/gh/actori/actori) [![crates.io](http://meritbadge.herokuapp.com/actori)](https://crates.io/crates/actori) [![Join the chat at https://gitter.im/actori/actori](https://badges.gitter.im/actori/actori.svg)](https://gitter.im/actori/actori?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Actori is a Rust actors framework. * [User Guide](https://actori.rs/book/actori/) * [API Documentation (Development)](http://actori.github.io/actori/actori/) * [API Documentation (Releases)](https://docs.rs/actori/) * Cargo package: [actori](https://crates.io/crates/actori) * Minimum supported Rust version: 1.39 or later --- ## Features * Async/Sync actors. * Actor communication in a local/thread context. * Uses [Futures](https://crates.io/crates/futures) for asynchronous message handling. * HTTP1/HTTP2 support ([actori-web](https://github.com/actori/actori-web)) * Actor supervision. * Typed messages (No `Any` type). ## Usage To use `actori`, add this to your `Cargo.toml`: ```toml [dependencies] actori = "0.9" ``` ### Initialize Actori In order to use actori you first need to create a `System`. ```rust,ignore fn main() { let system = actori::System::new("test"); system.run(); } ``` Actori uses the [tokio](https://github.com/tokio-rs/tokio) event loop. `System::new()` creates a new event loop and starts the `System` actor. `system.run()` starts the tokio event loop, and will finish once the `System` actor receives the `SystemExit` message. Let's create a simple Actor. ### Implement an Actor In order to define an actor you need to define a struct and have it implement the [`Actor`](https://actori.github.io/actori/actori/trait.Actor.html) trait. ```rust use actori::{Actor, Addr, Arbiter, Context, System}; struct MyActor; impl Actor for MyActor { type Context = Context; fn started(&mut self, ctx: &mut Self::Context) { println!("I am alive!"); System::current().stop(); // <- stop system } } fn main() { let system = System::new("test"); let addr = MyActor.start(); system.run(); } ``` Spawning a new actor is achieved via the `start` and `create` methods of the [Actor](https://actori.github.io/actori/actori/trait.Actor.html) trait. It provides several different ways of creating actors, for details check docs. You can implement `started`, `stopping` and `stopped` methods of the Actor trait. `started` gets called when actor starts and `stopping` when actor finishes. Check [API documentation](https://actori.github.io/actori/actori/trait.Actor.html#actor-lifecycle) for more information on the actor lifecycle. ### Handle messages An Actor communicates with another Actor by sending messages. In actori all messages are typed. Let's define a simple `Sum` message with two `usize` parameters, and an actor which will accept this message and return the sum of those two numbers. ```rust use futures::{future, Future}; use actori::*; // this is our Message struct Sum(usize, usize); // we have to define the response type for `Sum` message impl Message for Sum { type Result = usize; } // Actor definition struct Summator; impl Actor for Summator { type Context = Context; } // now we need to define `MessageHandler` for the `Sum` message. impl Handler for Summator { type Result = usize; // <- Message response type fn handle(&mut self, msg: Sum, ctx: &mut Context) -> Self::Result { msg.0 + msg.1 } } fn main() { let sys = System::new("test"); let addr = Summator.start(); let res = addr.send(Sum(10, 5)); // <- send message and get future for result Arbiter::spawn(res.then(|res| { match res { Ok(result) => println!("SUM: {}", result), _ => println!("Something wrong"), } System::current().stop(); future::result(Ok(())) })); sys.run(); } ``` All communications with actors go through an `Addr` object. You can `do_send` a message without waiting for a response, or `send` an actor with specific message. The `Message` trait defines the result type for a message. ### Actor state and subscription for specific messages You may have noticed that methods of `Actor` and `Handler` traits accept `&mut self`, so you are welcome to store anything in an actor and mutate it whenever necessary. Address objects require an actor type, but if we just want to send a specific message to an actor that can handle the message, we can use the `Recipient` interface. Let's create a new actor that uses `Recipient`. ```rust use std::time::Duration; use actori::prelude::*; #[derive(Message)] struct Ping { pub id: usize } // Actor definition struct Game { counter: usize, addr: Recipient, } impl Actor for Game { type Context = Context; } // simple message handler for Ping message impl Handler for Game { type Result = (); fn handle(&mut self, msg: Ping, ctx: &mut Context) { self.counter += 1; if self.counter > 10 { System::current().stop(); } else { println!("Ping received {:?}", msg.id); // wait 100 nanos ctx.run_later(Duration::new(0, 100), move |act, _| { act.addr.do_send(Ping{id: msg.id + 1}); }); } } } fn main() { let system = System::new("test"); // To get a Recipient object, we need to use a different builder method // which will allow postponing actor creation let addr = Game::create(|ctx| { // now we can get an address of the first actor and create the second actor let addr = ctx.address(); let addr2 = Game{counter: 0, addr: addr.recipient()}.start(); // let's start pings addr2.do_send(Ping{id: 10}); // now we can finally create first actor Game{counter: 0, addr: addr2.recipient()} }); system.run(); } ``` ### chat example There is a [chat example](https://github.com/actori/actori/tree/master/examples/chat) which provides a basic example of networking client/server service. ### fectl You may consider checking out [fectl](https://github.com/fafhrd91/fectl) utility. It is written with `actori` and shows how to create networking application with relatively complex interactions. ## Contributing All contributions are welcome, if you have a feature request don't hesitate to open an issue! ## License This project is licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ## Code of Conduct Contribution to the actori crate is organized under the terms of the Contributor Covenant, the maintainer of actori, @fafhrd91, promises to intervene to uphold that code of conduct.