gg-cli 0.41.0

GG - Gui for JJ
//! Typed handlers for web mode queries

use std::path::PathBuf;
use std::sync::mpsc::channel;

use axum::{Json, Router, extract::State, routing::post};
use serde::Deserialize;

use crate::messages::{
    RepoConfig, RepoStatus, RevSet,
    queries::{LogPage, RevsResult},
};
use crate::worker::SessionEvent;

use super::ApiError;
use super::state::AppState;

pub fn router() -> Router<AppState> {
    Router::new()
        .route("/query_workspace", post(query_workspace))
        .route("/query_log", post(query_log))
        .route("/query_log_next_page", post(query_log_next_page))
        .route("/query_revisions", post(query_revisions))
        .route("/query_remotes", post(query_remotes))
        .route("/query_recent_workspaces", post(query_recent_workspaces))
        .route("/query_snapshot", post(query_snapshot))
}

#[derive(Deserialize)]
pub struct QueryWorkspace {
    path: Option<String>,
}

async fn query_workspace(
    State(state): State<AppState>,
    Json(req): Json<QueryWorkspace>,
) -> Result<Json<RepoConfig>, ApiError> {
    let path = req.path.map(PathBuf::from);
    let (tx, rx) = channel();
    state.worker_tx.send(SessionEvent::OpenWorkspace {
        tx,
        wd: path.clone(),
    })?;

    let config = match rx.recv()? {
        Ok(config) => {
            log::debug!("load workspace succeeded");
            config
        }
        Err(err) => {
            log::warn!("load workspace failed: {err}");
            RepoConfig::LoadError {
                absolute_path: path.unwrap_or_default().into(),
                message: format!("{:#}", err),
            }
        }
    };

    Ok(Json(config))
}

#[derive(Deserialize)]
pub struct QueryLog {
    revset: String,
}

async fn query_log(
    State(state): State<AppState>,
    Json(req): Json<QueryLog>,
) -> Result<Json<LogPage>, ApiError> {
    let (tx, rx) = channel();
    state.worker_tx.send(SessionEvent::QueryLog {
        tx,
        query: req.revset,
    })?;
    let result = rx.recv()??;
    Ok(Json(result))
}

async fn query_log_next_page(State(state): State<AppState>) -> Result<Json<LogPage>, ApiError> {
    let (tx, rx) = channel();
    state
        .worker_tx
        .send(SessionEvent::QueryLogNextPage { tx })?;
    let result = rx.recv()??;
    Ok(Json(result))
}

#[derive(Deserialize)]
pub struct QueryRevisions {
    set: RevSet,
}

async fn query_revisions(
    State(state): State<AppState>,
    Json(req): Json<QueryRevisions>,
) -> Result<Json<RevsResult>, ApiError> {
    let (tx, rx) = channel();
    state
        .worker_tx
        .send(SessionEvent::QueryRevisions { tx, set: req.set })?;
    let result = rx.recv()??;
    Ok(Json(result))
}

#[derive(Deserialize)]
pub struct QueryRemotes {
    tracking_bookmark: Option<String>,
}

async fn query_remotes(
    State(state): State<AppState>,
    Json(req): Json<QueryRemotes>,
) -> Result<Json<Vec<String>>, ApiError> {
    let (tx, rx) = channel();
    state.worker_tx.send(SessionEvent::QueryRemotes {
        tx,
        tracking_bookmark: req.tracking_bookmark,
    })?;
    let result = rx.recv()??;
    Ok(Json(result))
}

async fn query_recent_workspaces(
    State(state): State<AppState>,
) -> Result<Json<Vec<String>>, ApiError> {
    let (tx, rx) = channel();
    state.worker_tx.send(SessionEvent::ReadConfigArray {
        tx,
        key: vec!["gg".into(), "ui".into(), "recent-workspaces".into()],
    })?;
    let result = rx.recv()?.unwrap_or_default();
    Ok(Json(result))
}

async fn query_snapshot(
    State(state): State<AppState>,
) -> Result<Json<Option<RepoStatus>>, ApiError> {
    let (tx, rx) = channel();
    state.worker_tx.send(SessionEvent::ExecuteSnapshot { tx })?;
    let result = rx.recv()?;
    Ok(Json(result))
}