pub struct CommandRegistry { /* private fields */ }Expand description
The main entry point of the crate.
A registry is cheap to clone: internally it’s an Arc<Inner>.
Implementations§
Source§impl CommandRegistry
impl CommandRegistry
pub fn new(cfg: Config) -> Self
Sourcepub fn list_channels(&self) -> Vec<String>
pub fn list_channels(&self) -> Vec<String>
Returns the ids of every currently-registered channel, sorted.
Mirrors the TypeScript library’s listChannels() method.
Sourcepub fn list_commands(&self) -> Vec<CommandDef>
pub fn list_commands(&self) -> Vec<CommandDef>
Returns the full CommandDef (id + description + schema) for
every reachable command — local (non-private) and remote. Remote
defs are those advertised via register.command.request or
list.commands.response on the channel.
Mirrors the TypeScript library’s listCommands() method.
Results are sorted by id. A command id is only included once even
if both a local and remote entry exist (local wins).
Sourcepub async fn register_command<C: Command>(
&self,
cmd: C,
) -> Result<(), CommandError>
pub async fn register_command<C: Command>( &self, cmd: C, ) -> Result<(), CommandError>
Register a command on this registry.
The single registration entry point, covering both compile-time and runtime commands:
- Compile-time: pass an instance of a type that implements
Command. The#[command]/#[command_service]macros generate such types from a plainasync fn. - Runtime: pass a [
DynCommand] carrying owned id / description / schema and a closure handler.
Mirrors the TypeScript library’s registerCommand.
- Commands whose id starts with
_stay local: they are never escalated to arouter_channeland never advertised to peers vialist.commands.response. - Non-private commands are escalated upstream if this registry
has a
router_channel; the local entry is only committed after the router acks. - The advertised schema is normalized via
crate::schema::normalize_schemaon the way in, so every schema leaving the registry is language-agnostic JSON Schema regardless of how the caller built it.
Sourcepub async fn register_channel(
&self,
channel: Arc<dyn CommandChannel>,
) -> Result<impl Future<Output = ()> + Send + 'static, ChannelError>
pub async fn register_channel( &self, channel: Arc<dyn CommandChannel>, ) -> Result<impl Future<Output = ()> + Send + 'static, ChannelError>
Connects a CommandChannel to this registry.
Returns a driver future which must be polled by the caller’s
executor (via tokio::spawn, smol::spawn,
futures::executor::block_on, …) for the registry to exchange
messages with the peer. The future completes when the channel
closes.
Handler dispatch is concurrent within the driver task: the
pump pushes each incoming message’s handler future into a
FuturesUnordered and cooperatively interleaves them with
the next recv, so a slow handler that awaits external work
no longer blocks subsequent messages on the same channel. The
number of simultaneously in-flight handlers is capped by
Config::max_in_flight_per_channel (default 256); at the
cap the pump applies backpressure to the channel rather than
dropping messages. For true multi-thread parallelism, wrap
the handler body in your runtime’s spawn (e.g.
tokio::spawn) — the crate itself stays runtime-agnostic.
Sourcepub async fn execute<C: Command>(
&self,
request: C::Request,
) -> Result<C::Response, CommandError>
pub async fn execute<C: Command>( &self, request: C::Request, ) -> Result<C::Response, CommandError>
Executes a command identified by a compile-time Command type
— the strict form, giving the same compile-time type safety
that TypeScript’s strict-mode executeCommand<K> gives via the
CommandSchemaMap type parameter.
The command id comes from C::ID, the request type is pinned to
C::Request, and the response type is pinned to C::Response,
so the compiler rejects mismatches at the call site:
let sum: i64 = registry.execute::<MathAdd>(AddReq { a: 2, b: 3 }).await?;For commands whose id or payload shape is only known at runtime
(scripting hosts, FFI, plugins that advertise their own schema),
use execute_dyn.
Sourcepub async fn execute_dyn(
&self,
command_id: &str,
request: Value,
) -> Result<Value, CommandError>
pub async fn execute_dyn( &self, command_id: &str, request: Value, ) -> Result<Value, CommandError>
Executes a command whose id is only known at runtime — the
loose form, mirroring the TypeScript library’s
executeCommand(id, args) in loose mode.
Request and response are raw serde_json::Values, so this is
the canonical entry point for plugin hosts, scripting runtimes,
FFI bridges, and any code where the schema is discovered via
list_commands rather than declared at
compile time.
For statically-known commands, prefer execute
— it pins both types via the Command trait.
Sourcepub fn emit<E: Event>(&self, event: E) -> Result<(), CommandError>
pub fn emit<E: Event>(&self, event: E) -> Result<(), CommandError>
Emit an event. Dispatches to local listeners and — unless the
event id is private (starts with _) — broadcasts to every
connected channel.
Works for both compile-time events (#[event]-annotated
structs) and runtime events (DynEvent
— actually DynEvent). Id, description,
and schema are all read off the event instance.
Sourcepub fn on<E: Event + DeserializeOwned>(
&self,
listener: impl Fn(E) + Send + Sync + 'static,
) -> impl FnOnce() + Send + Sync + 'static
pub fn on<E: Event + DeserializeOwned>( &self, listener: impl Fn(E) + Send + Sync + 'static, ) -> impl FnOnce() + Send + Sync + 'static
Subscribe a typed listener. The callback receives a
deserialized E every time an event with id E::ID fires,
whether emitted locally or received from a connected channel.
Returns an unsubscribe closure — call it (and drop it) to remove just this listener. Ignoring the return value is fine; the listener then lives for the life of the registry.
Listeners for the same event fire in insertion order. Payloads
that fail to deserialize into E are silently dropped for
this listener — they still flow to any typed-for-Value
listeners registered via on_dyn.
Sourcepub fn on_dyn<F>(
&self,
event_id: impl Into<String>,
listener: F,
) -> impl FnOnce() + Send + Sync + 'static
pub fn on_dyn<F>( &self, event_id: impl Into<String>, listener: F, ) -> impl FnOnce() + Send + Sync + 'static
Sourcepub async fn dispose(&self)
pub async fn dispose(&self)
Tears down the registry: awaits close() on every connected
channel, drops every local and remote command, and clears all
event listeners. In-flight executes and register requests fail
with CommandError::ChannelDisconnected via the existing
channel-close path.
Mirrors the TypeScript library’s dispose(), but async so that
transports doing real teardown work (HTTP flush, MCP goodbye,
plugin sandbox shutdown) complete before this returns.
Callers normally don’t need this — dropping the last
CommandRegistry clone releases the inner state automatically
via Drop. Use dispose when a shared registry (held through
multiple clones) needs to be forcibly torn down, or in tests.
Trait Implementations§
Source§impl Clone for CommandRegistry
impl Clone for CommandRegistry
Source§fn clone(&self) -> CommandRegistry
fn clone(&self) -> CommandRegistry
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more