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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
use std::process::Stdio;
use bstr::{BStr, BString};
use crate::{
driver,
driver::{process, substitute_f_parameter, Operation, Process, State},
Driver,
};
/// The error returned by [State::maybe_launch_process()][super::State::maybe_launch_process()].
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("Failed to spawn driver: {command:?}")]
SpawnCommand {
source: std::io::Error,
command: std::process::Command,
},
#[error("Process handshake with command {command:?} failed")]
ProcessHandshake {
source: process::client::handshake::Error,
command: std::process::Command,
},
}
impl State {
/// Obtain a process as defined in `driver` suitable for a given `operation. `rela_path` may be used to substitute the current
/// file for use in the invoked `SingleFile` process.
///
/// Note that if a long-running process is defined, the `operation` isn't relevant and capabilities are to be checked by the caller.
pub fn maybe_launch_process(
&mut self,
driver: &Driver,
operation: Operation,
rela_path: &BStr,
) -> Result<Option<Process<'_>>, Error> {
match driver.process.as_ref() {
Some(process) => {
let client = match self.running.remove(process) {
Some(c) => c,
None => {
let (child, cmd) = spawn_driver(process.clone())?;
process::Client::handshake(child, "git-filter", &[2], &["clean", "smudge", "delay"]).map_err(
|err| Error::ProcessHandshake {
source: err,
command: cmd,
},
)?
}
};
// TODO: find a way to not have to do this 'borrow-dance'.
// this strangeness is to workaround the borrowchecker, who otherwise won't let us return a reader. Quite sad :/.
// One would want to `get_mut()` or insert essentially, but it won't work.
self.running.insert(process.clone(), client);
let client = self.running.get_mut(process).expect("just inserted");
Ok(Some(Process::MultiFile {
client,
key: driver::Key(process.to_owned()),
}))
}
None => {
let cmd = match operation {
Operation::Clean => driver
.clean
.as_ref()
.map(|cmd| substitute_f_parameter(cmd.as_ref(), rela_path)),
Operation::Smudge => driver
.smudge
.as_ref()
.map(|cmd| substitute_f_parameter(cmd.as_ref(), rela_path)),
};
let cmd = match cmd {
Some(cmd) => cmd,
None => return Ok(None),
};
let (child, command) = spawn_driver(cmd)?;
Ok(Some(Process::SingleFile { child, command }))
}
}
}
}
fn spawn_driver(cmd: BString) -> Result<(std::process::Child, std::process::Command), Error> {
let mut cmd: std::process::Command = gix_command::prepare(gix_path::from_bstr(cmd).into_owned())
.with_shell()
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.into();
let child = match cmd.spawn() {
Ok(child) => child,
Err(err) => {
return Err(Error::SpawnCommand {
source: err,
command: cmd,
})
}
};
Ok((child, cmd))
}