1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
use std::io;
use std::path::Path;
use std::process::Command;
use std::str;
use std::str::Utf8Error;
use regex::Regex;
#[derive(Debug, PartialEq)]
pub struct Blob<'a> {
pub object: &'a str,
pub path: &'a Path,
}
pub 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() {
let errstr = String::from_utf8(output.stderr)
.unwrap_or_else(|_| String::from("<could not format git error output as UTF8>"));
return Err(io::Error::new(
io::ErrorKind::Other,
format!("git ls-tree did not run successfully: {}", errstr),
));
}
Ok(output.stdout)
}
pub fn parse_ls_tree_output<'a>(output: &'a [u8]) -> Result<Vec<Blob<'a>>, Utf8Error> {
let re = Regex::new(r"^[^ ]+ [^ ]+ ([^\t]+)\t(.+)$").unwrap();
output.split(|byte| *byte == 0)
.map(str::from_utf8)
.filter_map(|r_l| match r_l {
Err(e) => Some(Err(e)),
Ok(l) => re.captures(l).map(Ok),
})
.map(|r_captures| r_captures.map(|captures| Blob {
object: captures.get(1).unwrap().as_str(),
path: Path::new(captures.get(2).unwrap().as_str()),
}))
.collect()
}
#[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);
}
}
|