use std::process::Command; use crate::bare::GitBare; use crate::error::{GitError, GitResult}; use crate::paginate; use crate::pb::{ ArchiveEntry, ListArchiveEntriesRequest, ListArchiveEntriesResponse, ObjectType, object_selector, }; impl GitBare { pub fn list_archive_entries( &self, request: ListArchiveEntriesRequest, ) -> GitResult { let revision = match request.treeish.and_then(|s| s.selector) { Some(object_selector::Selector::Oid(oid)) => { crate::sanitize::validate_oid_hex(&oid.hex)?; oid.hex } Some(object_selector::Selector::Revision(name)) => { crate::sanitize::validate_revision(&name.revision)?; name.revision } None => "HEAD".into(), }; let mut args = vec!["ls-tree".to_string(), "-r".into(), "-l".into(), revision]; if !request.pathspec.is_empty() { for path in &request.pathspec { crate::sanitize::validate_file_path(path)?; } args.push("--".into()); args.extend(request.pathspec.clone()); } let output = Command::new("git") .arg("--git-dir") .arg(&self.bare_dir) .args(&args) .output()?; if !output.status.success() { return Err(GitError::CommandFailed { status_code: output.status.code(), stderr: String::from_utf8_lossy(&output.stderr).into_owned(), }); } let entries = String::from_utf8_lossy(&output.stdout) .lines() .filter_map(|line| { let (meta, path) = line.split_once('\t')?; let parts: Vec<&str> = meta.split_whitespace().collect(); let hex = parts.get(2)?.to_string(); Some(ArchiveEntry { path: path.to_string(), oid: Some(self.oid_to_pb(hex)), mode: u32::from_str_radix(parts.first().copied().unwrap_or("0"), 8) .unwrap_or(0), size: parts.get(3).and_then(|s| s.parse().ok()).unwrap_or(0), r#type: match parts.get(1).copied().unwrap_or_default() { "tree" => ObjectType::Tree as i32, "blob" => ObjectType::Blob as i32, "commit" => ObjectType::Commit as i32, "tag" => ObjectType::Tag as i32, _ => ObjectType::Unspecified as i32, }, }) }) .collect::>(); let (entries, page_info) = paginate::paginate(&entries, request.pagination.as_ref()); Ok(ListArchiveEntriesResponse { entries, page_info: Some(page_info), }) } }