Skip to main content

routa_server/api/
clone_local.rs

1//! Local repository API - /api/clone/local
2//!
3//! POST /api/clone/local - Validate and load an existing local git repository
4
5use axum::{routing::post, Json, Router};
6use serde::Deserialize;
7
8use crate::api::repo_context::{normalize_local_repo_path, validate_local_git_repo_path};
9use crate::error::ServerError;
10use crate::git;
11use crate::state::AppState;
12
13pub fn router() -> Router<AppState> {
14    Router::new().route("/", post(load_local_repo))
15}
16
17#[derive(Debug, Deserialize)]
18struct LocalRepoRequest {
19    path: Option<String>,
20}
21
22async fn load_local_repo(
23    Json(body): Json<LocalRepoRequest>,
24) -> Result<Json<serde_json::Value>, ServerError> {
25    let raw_path = body
26        .path
27        .ok_or_else(|| ServerError::BadRequest("Missing 'path' field".into()))?;
28
29    let repo_path = normalize_local_repo_path(&raw_path);
30    validate_local_git_repo_path(&repo_path)?;
31
32    let repo_path_string = repo_path.to_string_lossy().to_string();
33    let (branch_info, status) = tokio::task::spawn_blocking({
34        let repo_path_string = repo_path_string.clone();
35        move || {
36            (
37                git::get_branch_info(&repo_path_string),
38                git::get_repo_status(&repo_path_string),
39            )
40        }
41    })
42    .await
43    .map_err(|error| ServerError::Internal(error.to_string()))?;
44
45    let name = repo_path
46        .file_name()
47        .and_then(|segment| segment.to_str())
48        .map(str::to_string)
49        .unwrap_or_else(|| repo_path_string.clone());
50
51    Ok(Json(serde_json::json!({
52        "success": true,
53        "name": name,
54        "path": repo_path_string,
55        "branch": branch_info.current,
56        "branches": branch_info.branches,
57        "status": status,
58    })))
59}