| 2992 | } |
| 2993 | |
| 2994 | async fn get_file_status() -> Result<Json<Vec<FileStatusInfo>>> { |
| 2995 | let cwd = std::env::current_dir() |
| 2996 | .map_err(|e| ApiError::BadRequest(format!("Failed to resolve current directory: {}", e)))?; |
| 2997 | let output = std::process::Command::new("git") |
| 2998 | .arg("-C") |
| 2999 | .arg(&cwd) |
| 3000 | .arg("status") |
| 3001 | .arg("--porcelain") |
| 3002 | .output() |
| 3003 | .map_err(|e| ApiError::BadRequest(format!("Failed to run git status: {}", e)))?; |
| 3004 | |
| 3005 | if !output.status.success() { |
| 3006 | return Ok(Json(Vec::new())); |
| 3007 | } |
| 3008 | |
| 3009 | let mut files = Vec::new(); |
| 3010 | for line in String::from_utf8_lossy(&output.stdout).lines() { |
| 3011 | if line.len() < 4 { |
| 3012 | continue; |
| 3013 | } |
| 3014 | let status_code = &line[..2]; |
| 3015 | let mut path = line[3..].trim().to_string(); |
| 3016 | if let Some((_, renamed_to)) = path.rsplit_once(" -> ") { |
| 3017 | path = renamed_to.to_string(); |
| 3018 | } |
| 3019 | |
| 3020 | let staged = status_code.chars().next().unwrap_or(' ') != ' '; |
| 3021 | let status_char = if staged { |
| 3022 | status_code.chars().next().unwrap_or(' ') |
| 3023 | } else { |
| 3024 | status_code.chars().nth(1).unwrap_or(' ') |
| 3025 | }; |
| 3026 | let status = match status_char { |
| 3027 | 'M' => "modified", |
| 3028 | 'A' => "added", |
| 3029 | 'D' => "deleted", |
| 3030 | 'R' => "renamed", |
| 3031 | 'C' => "copied", |
| 3032 | 'U' => "unmerged", |
| 3033 | '?' => "untracked", |
| 3034 | _ => "unknown", |
| 3035 | }; |
| 3036 | |
| 3037 | files.push(FileStatusInfo { |
| 3038 | path, |
| 3039 | status: status.to_string(), |
| 3040 | staged, |
| 3041 | }); |
| 3042 | } |
| 3043 | |
| 3044 | Ok(Json(files)) |
| 3045 | } |
| 3046 | |
| 3047 | #[derive(Debug, Serialize)] |
| 3048 | pub struct FileStatusInfo { |