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}