Skip to main content

corpora_engine/
oracle.rs

1//! Git-backed [`RevisionOracle`] — the trait lives in `corpora-core`; this is the IO impl.
2//! It shells out to `git` (no heavy VCS dependency); resolution is `git rev-parse`.
3
4use 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    /// Run `git -C <root> <args>`, returning trimmed stdout on success.
19    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    /// Is the `git` binary itself runnable? Checked without `-C` so a missing repo dir
34    /// doesn't masquerade as a missing binary.
35    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        // `^{commit}` forces the rev to name an actual commit; `--end-of-options` stops a
57        // rev that begins with `-` from being parsed as a flag.
58        let spec = format!("{}^{{commit}}", r.0);
59        self.run(&["rev-parse", "--verify", "--quiet", "--end-of-options", &spec])
60            .map(Rev)
61    }
62}