1use std::path::{Path, PathBuf};
2use std::process::Command;
3
4#[derive(Debug)]
5pub enum GitError {
6 NotInstalled,
7 NotARepo,
8 CommandFailed(String),
9}
10
11impl std::fmt::Display for GitError {
12 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13 match self {
14 GitError::NotInstalled => write!(f, "git is not installed"),
15 GitError::NotARepo => write!(f, "not a git repository"),
16 GitError::CommandFailed(msg) => write!(f, "git command failed: {}", msg),
17 }
18 }
19}
20
21impl std::error::Error for GitError {}
22
23pub fn ls_files(root: &Path) -> Result<Vec<PathBuf>, GitError> {
24 let output = Command::new("git")
25 .arg("ls-files")
26 .current_dir(root)
27 .output()
28 .map_err(|_| GitError::NotInstalled)?;
29
30 if !output.status.success() {
31 let stderr = String::from_utf8_lossy(&output.stderr);
32 if stderr.contains("not a git repository") {
33 return Err(GitError::NotARepo);
34 }
35 return Err(GitError::CommandFailed(stderr.to_string()));
36 }
37
38 let stdout = String::from_utf8_lossy(&output.stdout);
39 let files = stdout
40 .lines()
41 .map(|line| PathBuf::from(line.trim()))
42 .collect();
43
44 Ok(files)
45}
46
47pub fn repo_root(path: &Path) -> Result<PathBuf, GitError> {
48 let output = Command::new("git")
49 .arg("rev-parse")
50 .arg("--show-toplevel")
51 .current_dir(path)
52 .output()
53 .map_err(|_| GitError::NotInstalled)?;
54
55 if !output.status.success() {
56 let stderr = String::from_utf8_lossy(&output.stderr);
57 if stderr.contains("not a git repository") {
58 return Err(GitError::NotARepo);
59 }
60 return Err(GitError::CommandFailed(stderr.to_string()));
61 }
62
63 let stdout = String::from_utf8_lossy(&output.stdout);
64 let root = PathBuf::from(stdout.trim());
65
66 Ok(root)
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use std::env;
73
74 #[test]
75 fn test_repo_root() {
76 let cwd = env::current_dir().unwrap();
77 let root = repo_root(&cwd);
78 assert!(root.is_ok());
79 }
80
81 #[test]
82 fn test_ls_files() {
83 let cwd = env::current_dir().unwrap();
84 let files = ls_files(&cwd);
85 assert!(files.is_ok());
86 }
87}