Skip to main content

wisp/runtime/
dispatcher.rs

1use crate::command::{Command, CommandResult, FailedCommand, FilesystemCommand, GitCommand, TerminalCommand};
2use crate::runtime::{agent, files, git};
3use acp_utils::client::AcpPromptHandle;
4use crossterm::{execute, style::Print};
5use std::io;
6
7use super::tasks::{ReadTask, TaskSupervisor};
8
9pub struct CommandDispatcher {
10    prompt_handle: AcpPromptHandle,
11    tasks: TaskSupervisor,
12}
13
14impl CommandDispatcher {
15    pub fn new(prompt_handle: AcpPromptHandle) -> Self {
16        Self { prompt_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.prompt_handle, command),
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}