1use std::path::PathBuf;
5use std::process::Command;
6
7use corpora_core::{OracleStatus, Rev, RevisionOracle};
8
9pub struct GitOracle {
10 root: PathBuf,
11}
12
13impl GitOracle {
14 pub fn new(root: impl Into<PathBuf>) -> Self {
15 GitOracle { root: root.into() }
16 }
17
18 fn run(&self, args: &[&str]) -> Option<String> {
20 let out = Command::new("git")
21 .arg("-C")
22 .arg(&self.root)
23 .args(args)
24 .output()
25 .ok()?;
26 if !out.status.success() {
27 return None;
28 }
29 let s = String::from_utf8_lossy(&out.stdout).trim().to_string();
30 (!s.is_empty()).then_some(s)
31 }
32
33 fn git_present(&self) -> bool {
36 Command::new("git")
37 .arg("--version")
38 .output()
39 .map(|o| o.status.success())
40 .unwrap_or(false)
41 }
42}
43
44impl RevisionOracle for GitOracle {
45 fn status(&self) -> OracleStatus {
46 if !self.git_present() {
47 OracleStatus::Unavailable
48 } else if self.run(&["rev-parse", "--git-dir"]).is_none() {
49 OracleStatus::NoRepo
50 } else {
51 OracleStatus::Ready
52 }
53 }
54
55 fn resolve(&self, r: &Rev) -> Option<Rev> {
56 let spec = format!("{}^{{commit}}", r.0);
59 self.run(&["rev-parse", "--verify", "--quiet", "--end-of-options", &spec])
60 .map(Rev)
61 }
62}