use std::{collections::HashMap, sync::Arc}; use axum::{ body::Body, extract::{Path, Query, State}, http::{Response, StatusCode}, }; use bytes::Bytes; use tokio::sync::Mutex; use tokio_stream::StreamExt as _; use tracing::debug; use crate::CodeState; #[tracing::instrument(skip(state, repository))] pub async fn info_refs( Path((_organization, repository)): Path<(String, String)>, Query(params): Query<HashMap<String, String>>, State(state): State<Arc<Mutex<CodeState>>>, ) -> Result<Response<Body>, StatusCode> { let state = state.lock().await; debug!("Received info refs request for repository: {}", repository); let repository_path = match state.get_repository(&repository) { Ok(path) => path, Err(_) => { debug!("Repository not found: {}", repository); return Ok(Response::builder() .status(StatusCode::NOT_FOUND) .body(Body::empty()) .unwrap()); } }; let workspace = match state.load_repository(&repository_path) { Ok(workspace) => workspace, Err(_) => { debug!("Failed to get workspace for repository: {}", repository); return Ok(Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .body(Body::empty()) .unwrap()); } }; let view = workspace.1.view(); // Create a info refs response let mut info_refs = Vec::new(); info_refs.push("# service=git-upload-pack\n".to_string()); for (name, reference_target) in view.git_refs().iter() { let Some(commit_id) = reference_target.as_normal() else { continue; }; debug!("Found reference: {} -> {}", name, commit_id.to_string()); let mut line = String::new(); line.push_str(commit_id.to_string().as_str()); line.push(' '); line.push_str(name.as_str()); line.push('\n'); info_refs.push(line); } let body = info_refs .iter() .map(|line| { let mut hex_line = String::new(); hex_line.push_str(&format!("{:06x}", line.len())); hex_line.push_str(line); hex_line }) .collect::<Vec<_>>() .join("") + "0000"; let response = Response::builder() .header( "Content-Type", "application/x-git-upload-pack-advertisement", ) .header("Cache-Control", "no-cache") .header("Pragma", "no-cache") .header("Expires", "0") .body(Body::from(body)) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(response) }