om_context/
git.rs

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}