routa_server/api/
clone_local.rs1use 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}