Skip to content

tiannian/serviceless

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Serviceless

Serviceless is a small async actor library for Rust, inspired by Actix-style APIs but kept minimal: one mailbox per [Service], fully async handlers, and addresses for typed messaging plus optional topic notifications.

The implementation of this crate does not use unsafe.

Features

  • Async actors — Each service runs a mailbox loop; started / stopped hooks and Handler::handle are async (use the async_trait crate).
  • Typed messages — Implement Message and Handler for your own types instead of manual routing tables.
  • call and sendServiceAddress::call awaits M::Result; send enqueues work and drops the handler return value.
  • Topics (pub/sub-style)Topic, RoutedTopic, and TopicEndpoint for one-shot subscribe / publish flows, still serialized through the actor mailbox.
  • External envelope streamsContext::with_stream merges another stream of Envelopes with the internal mailbox.
  • Typed narrowingServiceAddress::into_address builds a single-message-type Address plus a forwarding future you spawn next to the main run future.
  • Bring your own runtime — The library returns a run future; you spawn it (examples use Tokio). There are no optional Cargo [features] on this crate: the full API is always available.

Documentation

  • Run cargo doc --open -p serviceless for full API reference.
  • Narrative guide (actor usage, caveats, pub/sub): see the serviceless::docs module in the generated docs (overview, services, messaging, pub/sub, runtime).

Usage

Service

A Service is your actor type. You must set the associated Stream type (often EmptyStream<Self> when you only use the built-in mailbox).

use async_trait::async_trait;
use serviceless::{Context, EmptyStream, Service};

#[derive(Default)]
pub struct MyActor;

#[async_trait]
impl Service for MyActor {
    type Stream = EmptyStream<Self>;

    async fn started(&mut self, _ctx: &mut Context<Self, Self::Stream>) {
        // runs once before the mailbox loop
    }

    async fn stopped(&mut self, _ctx: &mut Context<Self, Self::Stream>) {
        // runs after the mailbox is closed
    }
}

Starting and stopping

Build a Context, start the service, then spawn the returned run future on your async runtime. Until that future is polled, mailbox work will not run.

use serviceless::Context;

# use async_trait::async_trait;
# use serviceless::{EmptyStream, Service};
# #[derive(Default)] struct MyActor;
# #[async_trait] impl Service for MyActor { type Stream = EmptyStream<Self>; }

let actor = MyActor::default();
let ctx = Context::new();
let (addr, run) = actor.start_by_context(ctx);
tokio::spawn(run);

Stop from inside the actor with Context::stop, or from outside with ServiceAddress::close_service.

Message and handler

Declare a Message (with type Result) and implement Handler for your service.

use async_trait::async_trait;
use serviceless::{Context, EmptyStream, Handler, Message, Service};

#[derive(Default)]
pub struct Service0;
#[async_trait]
impl Service for Service0 { type Stream = EmptyStream<Self>; }

pub struct U8(pub u8);

impl Message for U8 {
    type Result = U8;
}

#[async_trait]
impl Handler<U8> for Service0 {
    async fn handle(&mut self, message: U8, _ctx: &mut Context<Self, Self::Stream>) -> U8 {
        U8(message.0 + 2)
    }
}

Address: call, send, and topics

ServiceAddress is cloneable and is how other tasks talk to the actor.

  • callasync; waits for M::Result. If the service has stopped, you get Error::ServiceStoped.
  • send — synchronous for the caller; still returns Result and drops the handler return value.
  • Preferred dispatchcall and send dispatch through Handler::handle_preferred. The default handle_preferred implementation calls handle, then uses ReplyHandle for replies. You can override handle_preferred to spawn a task and reply later so the current handler path does not block mailbox progress (see actor/examples/preferred.rs).
  • subscribe — synchronous enqueue with a topic key argument; returns Result of a Future you await for the next matching publication (see serviceless::docs::pubsub and examples/topic.rs).

Examples

Runnable examples live under actor/examples/ (e.g. topic.rs, single.rs, external_stream.rs, preferred.rs).

cargo run -p serviceless --example topic

About

An simple actor model in rust, like actix

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages