aether-wisp 0.7.18

A terminal UI for AI coding agents via the Agent Client Protocol (ACP)
Documentation
use crate::attachment::{AttachmentOutcome, build_attachments};
use crate::command::{CommandResult, FilesystemCommand};
use crate::file_index::index_files;
use crate::settings::{list_theme_files, review_theme_choices, save_settings};
use crate::theme::{Theme, ThemeApplicationError};
use tokio::task::spawn_blocking;

pub(super) async fn execute(command: FilesystemCommand) -> CommandResult {
    match command {
        FilesystemCommand::IndexFiles { request_id, root } => {
            let indexed = |files| CommandResult::FilesIndexed { request_id, files };
            run_or_fail("Failed to index files", move || index_files(&root), indexed).await
        }
        FilesystemCommand::PrepareSubmission { attachments } => {
            let outcome = spawn_blocking(move || build_attachments(&attachments)).await.unwrap_or_else(|error| {
                AttachmentOutcome {
                    blocks: Vec::new(),
                    warnings: vec![format!("Could not prepare attachments: {error}")],
                }
            });
            CommandResult::SubmissionPrepared(outcome)
        }
        FilesystemCommand::ListThemes => {
            run_or_fail("Failed to list themes", list_theme_files, CommandResult::ThemesListed).await
        }
        FilesystemCommand::ListReviewThemes => {
            run_or_fail("Failed to load review themes", review_theme_choices, CommandResult::ReviewThemesListed).await
        }
        FilesystemCommand::ApplyTheme { settings } => CommandResult::ThemeApplied(
            spawn_blocking(move || {
                let theme = Theme::load_selection(&settings.theme)?;
                save_settings(&settings).map_err(ThemeApplicationError::Save)?;
                Ok((settings, theme))
            })
            .await
            .map_err(ThemeApplicationError::Task)
            .and_then(std::convert::identity),
        ),
    }
}

async fn run_or_fail<T: Send + 'static>(
    label: &str,
    work: impl FnOnce() -> T + Send + 'static,
    done: impl FnOnce(T) -> CommandResult,
) -> CommandResult {
    match spawn_blocking(work).await {
        Ok(value) => done(value),
        Err(error) => CommandResult::BackgroundFailed(format!("{label}: {error}")),
    }
}