27 stable releases (7 major)

8.0.2 Jun 21, 2023
7.0.1 Jun 2, 2023
6.0.1 May 26, 2023
5.0.4 Mar 24, 2023
1.1.2 Nov 2, 2022

#76 in #async-trait

Download history 1/week @ 2024-06-16 6/week @ 2024-06-30 77/week @ 2024-07-07 5/week @ 2024-07-28 3/week @ 2024-08-25 2/week @ 2024-09-01 34/week @ 2024-09-22 23/week @ 2024-09-29

57 downloads per month
Used in 2 crates (via vin)

Custom license

37KB
736 lines

vin

A lightweight, ergonomic and unconventional actor crate.

Crates.io Documentation MIT licensed

[dependencies]
vin = "9.1"

Overview

Vin's goal is to be an ergonomic, unconventional actor library. Vin doesn't follow the conventional implementations for actor libraries, but tries to be as simple as possible, while still providing an ergonomic and rich interface by integrating itself with tokio as much as possible. Each actor gets its own task to poll messages and execute handlers on. Its address is shared by a simple Arc. Vin also provides a way to gracefully shutdown all actors without having to do the manual labour yourself. Actor data is stored in its actor context and is retrievable for reading with Actor::ctx() and for writing with Actor::ctx_mut() which acquire a RwLock to the data. Vin also provides a "task actor" which is simply a tokio task spun up and synchronized with Vin's shutdown system.

Vin completely relies on tokio (for the async runtime), log (for diagnostics) and async_trait.

Examples

Regular actors

Basic usage of vin.

use vin::prelude::*;
use std::time::Duration;
use tracing::Level;

#[vin::message]
#[derive(Debug, Clone)]
pub enum Msg {
    Foo,
    Bar,
    Baz,
}

#[vin::message(result = u32, error = String)]
#[derive(Debug, Clone)]
pub struct MsgWithResult;

#[vin::actor]
#[vin::handles(Msg)]
struct MyActor {
    pub number: u32,
}

#[async_trait]
impl vin::Hooks for MyActor {}

#[async_trait]
impl vin::Handler<Msg> for MyActor {
    async fn handle(&self, msg: Msg) -> Result<(), ()> {
        let ctx = self.ctx().await;
        println!("The message is: {:?} and the number is {}", msg, ctx.number);

        Ok(())
    }
}

#[async_trait]
impl vin::Handler<MsgWithResult> for MyActor {
    async fn handle(&self, MsgWithResult(should_err): MsgWithResult) -> Result<u32, String> {
        if should_err { Err("error".to_string()) }
        else { Ok(42) }
    }
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt()
        .with_max_level(Level::TRACE)
        .init();

    let ctx = VinContextMyActor { number: 42 };
    let actor = MyActor::start("test", ctx).unwrap();
    actor.send(Msg::Bar).await;
    tokio::time::sleep(Duration::from_millis(500)).await;
    let res = actor.send_and_wait(MsgWithResult(false)).await.unwrap();
    assert_eq!(res, 42);
    vin::shutdown();
    vin::wait_for_shutdowns().await;
}

Task actors

Basic usage of task actors in vin.

use vin::*;
use std::time::Duration;
use tracing::Level;

#[vin::task]
#[derive(Debug, Clone, PartialEq, Eq)]
struct MyTaskActor {
    pub number: u32,
}

#[async_trait]
impl vin::Task for MyTaskActor {
    async fn task(self) -> anyhow::Result<()> {
        for i in 0..self.number {
            log::info!("{}. iteration", i);
        }

        Ok(())
    }
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt()
        .with_max_level(Level::TRACE)
        .init();

    let ctx = VinContextMyTaskActor { number: 5 };
    let actor = MyTaskActor::start("test_task", ctx);
    tokio::time::sleep(Duration::from_millis(100)).await;
    actor.close();
    vin::shutdown();
    vin::wait_for_shutdowns().await;
}

License

This project is licensed under the MIT license.

Dependencies

~2MB
~44K SLoC