gix_filter/driver/
init.rs

1use std::process::Stdio;
2
3use bstr::{BStr, BString};
4
5use crate::{
6    driver,
7    driver::{process, substitute_f_parameter, Operation, Process, State},
8    Driver,
9};
10
11/// The error returned by [State::maybe_launch_process()][super::State::maybe_launch_process()].
12#[derive(Debug, thiserror::Error)]
13#[allow(missing_docs)]
14pub enum Error {
15    #[error("Failed to spawn driver: {command:?}")]
16    SpawnCommand {
17        source: std::io::Error,
18        command: std::process::Command,
19    },
20    #[error("Process handshake with command {command:?} failed")]
21    ProcessHandshake {
22        source: process::client::handshake::Error,
23        command: std::process::Command,
24    },
25}
26
27/// Lifecycle
28impl State {
29    /// Obtain a process as defined in `driver` suitable for a given `operation. `rela_path` may be used to substitute the current
30    /// file for use in the invoked `SingleFile` process.
31    ///
32    /// Note that if a long-running process is defined, the `operation` isn't relevant and capabilities are to be checked by the caller.
33    pub fn maybe_launch_process(
34        &mut self,
35        driver: &Driver,
36        operation: Operation,
37        rela_path: &BStr,
38    ) -> Result<Option<Process<'_>>, Error> {
39        match driver.process.as_ref() {
40            Some(process) => {
41                let client = match self.running.remove(process) {
42                    Some(c) => c,
43                    None => {
44                        let (child, cmd) = spawn_driver(process.clone(), &self.context)?;
45                        process::Client::handshake(child, "git-filter", &[2], &["clean", "smudge", "delay"]).map_err(
46                            |err| Error::ProcessHandshake {
47                                source: err,
48                                command: cmd,
49                            },
50                        )?
51                    }
52                };
53
54                // TODO: find a way to not have to do this 'borrow-dance'.
55                // this strangeness is to workaround the borrowchecker, who otherwise won't let us return a reader. Quite sad :/.
56                // One would want to `get_mut()` or insert essentially, but it won't work.
57                self.running.insert(process.clone(), client);
58                let client = self.running.get_mut(process).expect("just inserted");
59
60                Ok(Some(Process::MultiFile {
61                    client,
62                    key: driver::Key(process.to_owned()),
63                }))
64            }
65            None => {
66                let cmd = match operation {
67                    Operation::Clean => driver
68                        .clean
69                        .as_ref()
70                        .map(|cmd| substitute_f_parameter(cmd.as_ref(), rela_path)),
71
72                    Operation::Smudge => driver
73                        .smudge
74                        .as_ref()
75                        .map(|cmd| substitute_f_parameter(cmd.as_ref(), rela_path)),
76                };
77
78                let cmd = match cmd {
79                    Some(cmd) => cmd,
80                    None => return Ok(None),
81                };
82
83                let (child, command) = spawn_driver(cmd, &self.context)?;
84                Ok(Some(Process::SingleFile { child, command }))
85            }
86        }
87    }
88}
89
90fn spawn_driver(
91    cmd: BString,
92    context: &gix_command::Context,
93) -> Result<(std::process::Child, std::process::Command), Error> {
94    let mut cmd: std::process::Command = gix_command::prepare(gix_path::from_bstr(cmd).into_owned())
95        .command_may_be_shell_script()
96        .with_context(context.clone())
97        .stdin(Stdio::piped())
98        .stdout(Stdio::piped())
99        .stderr(Stdio::inherit())
100        .into();
101    gix_trace::debug!(cmd = ?cmd, "launching filter driver");
102    let child = match cmd.spawn() {
103        Ok(child) => child,
104        Err(err) => {
105            return Err(Error::SpawnCommand {
106                source: err,
107                command: cmd,
108            })
109        }
110    };
111    Ok((child, cmd))
112}