wisp/runtime/
dispatcher.rs1use crate::command::{Command, CommandResult, FailedCommand, FilesystemCommand, GitCommand, TerminalCommand};
2use crate::runtime::{agent, files, git};
3use acp_utils::client::AcpClientHandle;
4use crossterm::{execute, style::Print};
5use std::io;
6
7use super::tasks::{ReadTask, TaskSupervisor};
8
9pub struct CommandDispatcher {
10 client_handle: AcpClientHandle,
11 tasks: TaskSupervisor,
12}
13
14impl CommandDispatcher {
15 pub fn new(client_handle: AcpClientHandle) -> Self {
16 Self { client_handle, tasks: TaskSupervisor::default() }
17 }
18
19 pub fn dispatch(&mut self, command: Command) -> Option<CommandResult> {
20 match command {
21 Command::Agent(command) => agent::execute(&self.client_handle, command, &mut self.tasks),
22 Command::Filesystem(command) => {
23 let key = match &command {
24 FilesystemCommand::IndexFiles { .. } => Some(ReadTask::FileIndex),
25 FilesystemCommand::PrepareSubmission { .. } => Some(ReadTask::AttachmentPreparation),
26 FilesystemCommand::ListThemes => Some(ReadTask::ThemeList),
27 FilesystemCommand::ApplyTheme { .. } => None,
28 };
29 let work = async move { files::execute(command).await };
30 if let Some(key) = key {
31 self.tasks.spawn_read(key, work);
32 } else {
33 self.tasks.spawn_mutation(work);
34 }
35 None
36 }
37 Command::Git(command) => {
38 let read = matches!(command, GitCommand::Load { .. } | GitCommand::LoadFullFile { .. });
39 let work = async move { CommandResult::GitDiff(git::execute(command).await) };
40 if read {
41 self.tasks.spawn_read(ReadTask::GitReview, work);
42 } else {
43 self.tasks.spawn_mutation(work);
44 }
45 None
46 }
47 Command::ResolveWorkspace { cwd } => {
48 self.tasks.spawn_read(ReadTask::Workspace, async move {
49 let status = git::resolve_workspace_status(&cwd).await;
50 CommandResult::WorkspaceResolved { cwd, status }
51 });
52 None
53 }
54 Command::Terminal(command) => execute_terminal(&command),
55 }
56 }
57
58 pub fn has_pending_tasks(&self) -> bool {
59 !self.tasks.is_empty()
60 }
61
62 pub async fn next_result(&mut self) -> Option<CommandResult> {
63 self.tasks.next().await
64 }
65
66 pub async fn shutdown(&mut self) {
67 self.tasks.shutdown().await;
68 }
69}
70
71fn execute_terminal(command: &TerminalCommand) -> Option<CommandResult> {
72 let TerminalCommand::RingBell = command;
73 execute!(io::stdout(), Print("\x07")).err().map(|error| CommandResult::Failed {
74 command: FailedCommand::Other("ring the terminal bell"),
75 error: error.to_string(),
76 })
77}