gix_filter/driver/
mod.rs

1use std::collections::HashMap;
2
3use bstr::{BStr, BString, ByteSlice, ByteVec};
4
5///
6pub mod init;
7
8///
9pub mod apply;
10
11///
12pub mod shutdown;
13
14///
15pub mod delayed;
16
17///
18pub mod process;
19
20/// A literal driver process.
21pub enum Process<'a> {
22    /// A spawned processes to handle a single file
23    SingleFile {
24        /// The child to use as handle for sending and receiving data.
25        child: std::process::Child,
26        /// The launched command that produced the `child` in the first place
27        command: std::process::Command,
28    },
29    /// A multi-file process which is launched once to handle one or more files by using a custom IO protocol.
30    MultiFile {
31        /// A handle to interact with the long-running process.
32        client: &'a mut process::Client,
33        /// A way to refer to the `client` later if needed.
34        key: Key,
35    },
36}
37
38/// The kind of operation to apply using a driver
39#[derive(Debug, Copy, Clone)]
40pub enum Operation {
41    /// Turn worktree content into content suitable for storage in `git`.
42    Clean,
43    /// Turn content stored in `git` to content suitable for the working tree.
44    Smudge,
45}
46
47impl Operation {
48    /// Return a string that identifies the operation. This happens to be the command-names used in long-running processes as well.
49    pub fn as_str(&self) -> &'static str {
50        match self {
51            Operation::Clean => "clean",
52            Operation::Smudge => "smudge",
53        }
54    }
55}
56
57/// State required to handle `process` filters, which are running until all their work is done.
58///
59/// These can be significantly faster on some platforms as they are launched only once, while supporting asynchronous processing.
60///
61/// ### Lifecycle
62///
63/// Note that [`shutdown()`][State::shutdown()] must be called to finalize long-running processes.
64/// Failing to do so will naturally shut them down by terminating their pipes, but finishing explicitly
65/// allows to wait for processes as well.
66#[derive(Default)]
67pub struct State {
68    /// The list of currently running processes. These are preferred over simple clean-and-smudge programs.
69    ///
70    /// Note that these processes are expected to shut-down once their stdin/stdout are dropped, so nothing else
71    /// needs to be done to clean them up after drop.
72    running: HashMap<BString, process::Client>,
73
74    /// The context to pass to spawned filter programs.
75    pub context: gix_command::Context,
76}
77
78/// Initialization
79impl State {
80    /// Create a new instance using `context` to inform launched processes about their environment.
81    pub fn new(context: gix_command::Context) -> Self {
82        Self {
83            running: Default::default(),
84            context,
85        }
86    }
87}
88
89impl Clone for State {
90    fn clone(&self) -> Self {
91        State {
92            running: Default::default(),
93            context: self.context.clone(),
94        }
95    }
96}
97
98/// A way to reference a running multi-file filter process for later acquisition of delayed output.
99#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
100pub struct Key(BString);
101
102/// Substitute `path` as shell-save version into `cmd` which could be something like `cmd something %f`.
103fn substitute_f_parameter(cmd: &BStr, path: &BStr) -> BString {
104    let mut buf: BString = Vec::with_capacity(cmd.len()).into();
105
106    let mut ofs = 0;
107    while let Some(pos) = cmd[ofs..].find(b"%f") {
108        buf.push_str(&cmd[..ofs + pos]);
109        buf.extend_from_slice(&gix_quote::single(path));
110        ofs += pos + 2;
111    }
112    buf.push_str(&cmd[ofs..]);
113    buf
114}