loopflow 0.10.0

Run steps and flows with coding agents
Documentation
use axum::extract::{Query, State};
use axum::http::StatusCode;
use axum::Json;
use serde::Deserialize;

use crate::engine::worktrees::{list_worktrees, main_repo_root, wave_name_from_worktree_and_main};
use crate::lfd::http::dto::{ListResponse, WorktreeDto};
use crate::lfd::http::state::HttpState;
use crate::lfd::http::{api_error, map_store_error, ApiMessage, ApiResult};

#[derive(Debug, Deserialize)]
pub struct ListWorktreesQuery {
    repo: String,
}

fn internal_error(
    err: impl std::fmt::Display,
) -> (StatusCode, Json<crate::lfd::http::dto::ErrorResponse>) {
    api_error(
        StatusCode::INTERNAL_SERVER_ERROR,
        ApiMessage::Untrusted(err.to_string()),
    )
}

pub async fn list_worktrees_handler(
    State(state): State<HttpState>,
    Query(query): Query<ListWorktreesQuery>,
) -> ApiResult<ListResponse<WorktreeDto>> {
    let repo_path = std::path::PathBuf::from(&query.repo);
    let main_repo = main_repo_root(&repo_path).unwrap_or_else(|_| repo_path.clone());
    let repo_str = main_repo.to_string_lossy().to_string();
    let waves = state
        .store
        .list_waves(Some(&repo_str))
        .await
        .map_err(map_store_error)?;

    let wave_name_to_id: std::collections::HashMap<String, String> = waves
        .into_iter()
        .map(|w| (w.name().clone(), w.id().to_string()))
        .collect();

    let worktrees = tokio::task::spawn_blocking({
        let repo = main_repo.clone();
        move || list_worktrees(&repo)
    })
    .await
    .map_err(internal_error)?
    .map_err(internal_error)?;

    let dtos: Vec<WorktreeDto> = worktrees
        .into_iter()
        .filter(|wt| wt.path != main_repo)
        .map(|wt| {
            let wave_id = wave_name_from_worktree_and_main(&wt.path, &main_repo)
                .and_then(|name| wave_name_to_id.get(&name).cloned());

            WorktreeDto {
                object: "worktree".to_string(),
                path: wt.path.to_string_lossy().to_string(),
                branch: wt.branch,
                merged: wt.merged,
                prunable: wt.prunable,
                wave_id,
            }
        })
        .collect();

    Ok(Json(ListResponse::new(dtos, false)))
}