Skip to main content

CommandRegistry

Struct CommandRegistry 

Source
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

Source

pub fn new(cfg: Config) -> Self

Source

pub fn id(&self) -> &str

Returns this registry’s identifier.

Source

pub fn list_channels(&self) -> Vec<String>

Returns the ids of every currently-registered channel, sorted.

Mirrors the TypeScript library’s listChannels() method.

Source

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).

Source

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 plain async 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 a router_channel and never advertised to peers via list.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_schema on the way in, so every schema leaving the registry is language-agnostic JSON Schema regardless of how the caller built it.
Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn on_dyn<F>( &self, event_id: impl Into<String>, listener: F, ) -> impl FnOnce() + Send + Sync + 'static
where F: Fn(Value) + Send + Sync + 'static,

Subscribe a dynamic listener by runtime id. The callback receives the raw JSON payload. Use this when the event id is only known at runtime (plugin runtimes, FFI, scripting hosts); prefer on whenever you have a compile-time Event type.

Same unsubscribe semantics as on.

Source

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

Source§

fn clone(&self) -> CommandRegistry

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.