swactor/crates/std/src/runtime_ext.rs
Zachery Aaron Shores-Chmielewski 61751f3054 feat: process primitives
Tools for treating groups of actors like an OS process.
2026-02-21 00:33:06 +07:00

155 lines
5.4 KiB
Rust

use swactor::actor::{ActorAddress, ActorInterface, EnvironmentBuilder, LogicalName, Message};
use swactor::runtime::Runtime;
use swactor::Error;
use crate::StdExtension;
fn get_ext(rt: &Runtime) -> &StdExtension {
rt.extension()
.expect("StdExtension not installed — use Runtime::with_extension()")
.as_any()
.downcast_ref::<StdExtension>()
.expect("Extension is not StdExtension")
}
/// Naming extension for [`Runtime`].
///
/// Provides `spawn_named`, `where_is`, `unregister`, and `registered_names`
/// via the [`StdExtension`] name registry.
pub trait RuntimeNaming {
/// Register a name for an already-spawned actor. Returns `Err` if name is taken.
fn register_name(&self, name: impl Into<String>, addr: ActorAddress) -> Result<(), Error>;
/// Spawn an actor with a registered name, returning its address.
fn spawn_named<A: ActorInterface>(&self, name: impl Into<String>, actor: A) -> Result<ActorAddress, Error>;
/// Look up an actor address by its registered name.
fn where_is(&self, name: &str) -> Option<ActorAddress>;
/// Unregister a name. Returns the address it was bound to, or `None`.
fn unregister(&self, name: &str) -> Option<ActorAddress>;
/// Return all currently registered actor names.
fn registered_names(&self) -> Vec<String>;
}
impl RuntimeNaming for Runtime {
fn register_name(&self, name: impl Into<String>, addr: ActorAddress) -> Result<(), Error> {
get_ext(self).name_registry.register(name.into(), addr)
}
fn spawn_named<A: ActorInterface>(&self, name: impl Into<String>, actor: A) -> Result<ActorAddress, Error> {
let name = name.into();
let env = EnvironmentBuilder::new()
.set(LogicalName(name.clone()))
.build();
let addr = self.spawn_with_env(actor, env)?;
if let Err(e) = get_ext(self).name_registry.register(name, addr) {
let _ = self.stop_actor(addr);
return Err(e);
}
Ok(addr)
}
fn where_is(&self, name: &str) -> Option<ActorAddress> {
get_ext(self).name_registry.lookup(name)
}
fn unregister(&self, name: &str) -> Option<ActorAddress> {
get_ext(self).name_registry.unregister(name)
}
fn registered_names(&self) -> Vec<String> {
get_ext(self).name_registry.registered_names()
}
}
/// Watching extension for [`Runtime`].
///
/// Provides `watch` / `unwatch` via the [`StdExtension`] watch registry.
pub trait RuntimeWatching {
/// Register a watch: `watcher` receives `ActorExited` when `target` dies.
fn watch(&self, watcher: ActorAddress, target: ActorAddress);
/// Cancel a watch.
fn unwatch(&self, watcher: ActorAddress, target: ActorAddress);
}
impl RuntimeWatching for Runtime {
fn watch(&self, watcher: ActorAddress, target: ActorAddress) {
get_ext(self).watch_registry.watch(watcher, target);
}
fn unwatch(&self, watcher: ActorAddress, target: ActorAddress) {
get_ext(self).watch_registry.unwatch(watcher, target);
}
}
/// Group extension for [`Runtime`].
///
/// Provides `join_group`, `leave_group`, `publish_to`, `group_members`,
/// and `groups` via the [`StdExtension`] group registry.
pub trait RuntimeGroups {
/// Add an actor to a named group. The group is created if it doesn't exist.
fn join_group(&self, addr: ActorAddress, group: impl Into<String>);
/// Remove an actor from a named group. Empty groups are auto-deleted.
fn leave_group(&self, addr: ActorAddress, group: &str);
/// Broadcast a message to all members of a named group.
/// Returns the number of messages successfully enqueued.
fn publish_to<M: Message>(&self, group: &str, msg: M) -> usize;
/// Return all current members of a named group.
fn group_members(&self, group: &str) -> Vec<ActorAddress>;
/// Return all active group names.
fn groups(&self) -> Vec<String>;
}
impl RuntimeGroups for Runtime {
fn join_group(&self, addr: ActorAddress, group: impl Into<String>) {
get_ext(self).group_registry.join(group.into(), addr);
}
fn leave_group(&self, addr: ActorAddress, group: &str) {
get_ext(self).group_registry.leave(group, &addr);
}
fn publish_to<M: Message>(&self, group: &str, msg: M) -> usize {
let members = get_ext(self).group_registry.members(group);
let mut count = 0;
for member in &members {
if self.send_to(*member, msg.clone()).is_ok() {
count += 1;
}
}
count
}
fn group_members(&self, group: &str) -> Vec<ActorAddress> {
get_ext(self).group_registry.members(group)
}
fn groups(&self) -> Vec<String> {
get_ext(self).group_registry.group_names()
}
}
/// Service registry extension for [`Runtime`].
///
/// Allows registering typed service bindings that are automatically injected
/// into every actor's environment at spawn time.
pub trait RuntimeResources {
/// Register a service address under marker type `S`.
///
/// All actors spawned after this call will have `ServiceBinding<S>` in
/// their environment (unless overridden via `spawn_builder`).
fn register_service<S: 'static + Send + Sync>(&self, addr: ActorAddress);
}
impl RuntimeResources for Runtime {
fn register_service<S: 'static + Send + Sync>(&self, addr: ActorAddress) {
get_ext(self).service_registry.register::<S>(addr);
}
}