summaryrefslogtreecommitdiffstats
path: root/src/git.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/git.rs')
-rw-r--r--src/git.rs71
1 files changed, 71 insertions, 0 deletions
diff --git a/src/git.rs b/src/git.rs
new file mode 100644
index 0000000..293f225
--- /dev/null
+++ b/src/git.rs
@@ -0,0 +1,71 @@
+use std::io;
+use std::path::Path;
+use std::process::Command;
+use std::str;
+use std::str::Utf8Error;
+
+use regex::Regex;
+
+#[derive(Debug, PartialEq)]
+struct Blob<'a> {
+ object: &'a str,
+ path: &'a Path,
+}
+
+fn crawl_git_tree<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
+ let output = Command::new("git")
+ .current_dir(path)
+ .args(&["ls-tree", "-zr", "HEAD"])
+ .output()?;
+
+ if !output.status.success() {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "git ls-tree did not run successfully",
+ ));
+ }
+
+ Ok(output.stdout)
+}
+
+fn parse_ls_tree_output<'a>(output: &'a [u8]) -> Result<Vec<Blob<'a>>, Utf8Error> {
+ let re = Regex::new(r"^[^ ]+ [^ ]+ ([^\t]+)\t(.+)$").unwrap();
+
+ let mut blobs = Vec::new();
+ for line in output.split(|&byte| byte == 0) {
+ let line = str::from_utf8(line)?;
+
+ if let Some(captures) = re.captures(line) {
+ blobs.push(Blob {
+ object: captures.get(1).unwrap().as_str(),
+ path: Path::new(captures.get(2).unwrap().as_str()),
+ })
+ }
+ }
+
+ Ok(blobs)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::git::{parse_ls_tree_output, Blob};
+
+ const TEST_OUTPUT: &[u8] = b"100644 blob 424965736fa85c814e0ecb12d42ec81021304ce9\tREADME.md\0invalid\0100644 blob 5ec8f34bce9b5b53f3d809591914fa1601534a8a src/main.rs\0";
+
+ #[test]
+ fn test_parse_ls_tree_output() {
+ let expected = vec![
+ Blob {
+ object: "424965736fa85c814e0ecb12d42ec81021304ce9",
+ path: Path::new("README.md"),
+ },
+ Blob {
+ object: "5ec8f34bce9b5b53f3d809591914fa1601534a8a",
+ path: Path::new("src/main.rs"),
+ },
+ ];
+
+ assert_eq!(parse_ls_tree_output(TEST_OUTPUT).unwrap(), expected);
+ }
+}