1use std::env::Args;
5use std::ffi::OsStr;
6use std::io::stdout;
7use std::io::BufRead as _;
8use std::io::BufReader;
9use std::io::Error;
10use std::io::ErrorKind;
11use std::io::Result;
12use std::io::Write as _;
13use std::ops::Deref as _;
14use std::process::Child;
15use std::process::ChildStdout;
16use std::process::Command;
17use std::process::Stdio;
18
19use diff_parse::File;
20
21
22pub const GIT: &str = "/usr/bin/git";
24
25
26pub fn await_child<S>(program: S, child: Child) -> Result<Option<ChildStdout>>
29where
30 S: AsRef<OsStr>,
31{
32 let mut child = child;
33
34 let status = child.wait()?;
35 if !status.success() {
36 let error = format!("process `{}` failed", program.as_ref().to_string_lossy());
37
38 if let Some(stderr) = child.stderr {
39 let mut stderr = BufReader::new(stderr);
40 let mut line = String::new();
41
42 if stderr.read_line(&mut line).is_ok() {
45 let line = line.trim();
46 return Err(Error::new(ErrorKind::Other, format!("{error}: {line}")))
47 }
48 }
49 return Err(Error::new(ErrorKind::Other, error))
50 }
51 Ok(child.stdout)
52}
53
54
55pub fn blame<A>(diffs: &[(File, File)], args: A) -> Result<()>
59where
60 A: Fn() -> Args,
61{
62 let out = stdout();
63 let mut out = out.lock();
64
65 for (src, dst) in diffs {
66 writeln!(out, "--- {}", src.file)?;
70 writeln!(out, "+++ {}", dst.file)?;
71 let () = out.flush()?;
74
75 let child = Command::new(GIT)
80 .arg("--no-pager")
81 .arg("blame")
82 .arg("-s")
83 .arg(format!("-L{},+{}", src.line, src.count))
84 .args(args().skip(1))
85 .arg("--")
86 .arg(src.file.deref())
87 .arg("HEAD")
88 .stdin(Stdio::null())
89 .stdout(Stdio::inherit())
90 .stderr(Stdio::piped())
91 .spawn()?;
92 let _ = await_child(GIT, child)?;
93 }
94 Ok(())
95}