//! Sausage: grow a system of WASI actors by talking to it. //! //! The host runs the broker, the native actors (`broker`, `cargo`, and the //! console), and one supervisor per WASM actor. See `PROTOCOL.md`. pub mod broker; pub mod candidate; pub mod cargo_actor; pub mod console; pub mod runtime; pub mod supervisor; pub mod vfs; pub mod world; use std::path::Path; use std::sync::Arc; use anyhow::{Result, bail}; use tokio::sync::mpsc; pub use broker::{Broker, Envelope}; pub use world::World; /// The contract between the host and the actors. The host publishes it at /// `/actor/broker/public/PROTOCOL.md`, so that every actor can read it. const PROTOCOL: &str = include_str!("../../PROTOCOL.md"); /// A running world. pub struct Host { pub world: World, pub broker: Arc, native: Vec>, _lock: std::fs::File, } impl Host { /// Start the broker, `cargo`, and every registered actor of the world at /// `root`. Only one host can run a world at a time. pub async fn start(root: &Path) -> Result { let world = World::new(std::path::absolute(root)?); if !world.exists() { bail!("{} is not a Sausage world. Create one with: sausage init", root.display()); } let lock = std::fs::File::create(world.root.join(".lock"))?; if lock.try_lock().is_err() { bail!("another sausage process runs {}", root.display()); } let broker = Broker::new(world.clone(), runtime::Runtime::new()?)?; world::write_atomic(&world.public_dir("broker").join("PROTOCOL.md"), PROTOCOL.as_bytes())?; let cargo = cargo_actor::Cargo::new(world.clone())?; let native = vec![ tokio::spawn(broker.clone().run_actor(broker.open_mailbox("broker"))), tokio::spawn(cargo.run_actor(broker.clone(), broker.open_mailbox(cargo_actor::ID))), ]; for change in broker.scan().await { eprintln!("sausage: {change}"); } Ok(Host { world, broker, native, _lock: lock }) } /// Register a native actor with the ID `id` and return its mailbox. pub fn mailbox(&self, id: &str) -> mpsc::Receiver { self.broker.open_mailbox(id) } /// Stop the broker and `cargo` (with the requests they still run), then /// every actor, gracefully. pub async fn stop(self) { for task in &self.native { task.abort(); } for task in self.native { let _ = task.await; } self.broker.shutdown_all().await; } }