reovim_driver_session/api/command.rs
1//! Command execution abstraction.
2//!
3//! This module provides traits for command execution:
4//! - [`CommandApi`] - For resolvers to execute commands
5//! - [`CommandHandle`] - Opaque handle for re-entrant command execution
6//! - [`CommandExecutor`] - Abstraction over runner's `CommandRegistry`
7//!
8//! # Design
9//!
10//! Following the mechanism vs policy principle:
11//! - **Session driver defines traits** (this module)
12//! - **Runner implements `CommandExecutor`** (via `CommandRegistry`)
13//! - **Modules use `CommandApi`** to execute commands
14//!
15//! This keeps the session driver decoupled from runner types.
16//!
17//! # Re-Entrant Execution (#547)
18//!
19//! `CommandExecutor::get_handle()` returns an owned `Arc<dyn CommandHandle>`,
20//! releasing the borrow on `self.executor`. Then `handle.execute(self, &ctx)`
21//! can pass `&mut self` cleanly. This enables commands to call other commands
22//! via `runtime.execute_command()`.
23//!
24//! # Example
25//!
26//! ```ignore
27//! use reovim_driver_session::api::CommandApi;
28//!
29//! fn execute_motion<S: CommandApi>(session: &mut S, motion_cmd: CommandId) {
30//! let ctx = CommandContext::new();
31//! let result = session.execute_command(motion_cmd, ctx);
32//! // Handle result...
33//! }
34//! ```
35
36use {
37 reovim_driver_command_types::{CommandContext, CommandResult},
38 reovim_kernel::api::v1::CommandId,
39 std::sync::Arc,
40};
41
42/// Command execution for resolvers.
43///
44/// Provides a way for resolvers to execute commands without
45/// knowing about the runner's command registry.
46pub trait CommandApi: Send {
47 /// Execute a command directly.
48 ///
49 /// The command is looked up and executed with the given context.
50 fn execute_command(&mut self, cmd: CommandId, ctx: CommandContext) -> CommandResult;
51}
52
53/// Opaque handle for executing a command with a `SessionRuntime`.
54///
55/// This trait bridges `CommandHandler` (in `reovim-driver-command`) and
56/// `SessionRuntime` (in this crate) without creating a dependency cycle.
57/// The server creates these by wrapping `Arc<dyn CommandHandler>` in a
58/// `HandlerBridge` wrapper (#547).
59pub trait CommandHandle: Send + Sync {
60 /// Execute the command with the given runtime and arguments.
61 fn execute(
62 &self,
63 runtime: &mut crate::SessionRuntime<'_>,
64 ctx: &CommandContext,
65 ) -> CommandResult;
66}
67
68/// Trait for command handler lookup.
69///
70/// Runner's `CommandRegistry` implements this trait.
71/// This abstraction prevents session driver from depending on runner types.
72///
73/// # Design
74///
75/// Instead of `SessionRuntime` holding a concrete `CommandRegistry`,
76/// it holds `&dyn CommandExecutor`. This maintains proper dependency
77/// direction: runner depends on session driver, not vice versa.
78///
79/// # Re-Entrant Execution (#547)
80///
81/// `get_handle()` returns an owned `Arc<dyn CommandHandle>`, releasing
82/// the borrow on the executor. The caller then invokes
83/// `handle.execute(runtime, ctx)` with full `&mut SessionRuntime` access.
84pub trait CommandExecutor: Send + Sync {
85 /// Look up a command handler by ID.
86 ///
87 /// Returns an `Arc` so the caller can execute the handler
88 /// through its own runtime (re-entrant execution).
89 /// Returns `None` if the command is not found.
90 fn get_handle(&self, id: &CommandId) -> Option<Arc<dyn CommandHandle>>;
91}
92#[cfg(test)]
93#[path = "tests/command.rs"]
94mod tests;