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 107 108 109 110 111 112 113 114 115 116 117 118 119
use std::collections::HashMap;
use bstr::{BStr, BString, ByteSlice, ByteVec};
///
#[allow(clippy::empty_docs)]
pub mod init;
///
#[allow(clippy::empty_docs)]
pub mod apply;
///
#[allow(clippy::empty_docs)]
pub mod shutdown;
///
#[allow(clippy::empty_docs)]
pub mod delayed;
///
#[allow(clippy::empty_docs)]
pub mod process;
/// A literal driver process.
pub enum Process<'a> {
/// A spawned processes to handle a single file
SingleFile {
/// The child to use as handle for sending and receiving data.
child: std::process::Child,
/// The launched command that produced the `child` in the first place
command: std::process::Command,
},
/// A multi-file process which is launched once to handle one or more files by using a custom IO protocol.
MultiFile {
/// A handle to interact with the long-running process.
client: &'a mut process::Client,
/// A way to refer to the `client` later if needed.
key: Key,
},
}
/// The kind of operation to apply using a driver
#[derive(Debug, Copy, Clone)]
pub enum Operation {
/// Turn worktree content into content suitable for storage in `git`.
Clean,
/// Turn content stored in `git` to content suitable for the working tree.
Smudge,
}
impl Operation {
/// Return a string that identifies the operation. This happens to be the command-names used in long-running processes as well.
pub fn as_str(&self) -> &'static str {
match self {
Operation::Clean => "clean",
Operation::Smudge => "smudge",
}
}
}
/// State required to handle `process` filters, which are running until all their work is done.
///
/// These can be significantly faster on some platforms as they are launched only once, while supporting asynchronous processing.
///
/// ### Lifecycle
///
/// Note that [`shutdown()`][State::shutdown()] must be called to finalize long-running processes.
/// Failing to do so will naturally shut them down by terminating their pipes, but finishing explicitly
/// allows to wait for processes as well.
#[derive(Default)]
pub struct State {
/// The list of currently running processes. These are preferred over simple clean-and-smudge programs.
///
/// Note that these processes are expected to shut-down once their stdin/stdout are dropped, so nothing else
/// needs to be done to clean them up after drop.
running: HashMap<BString, process::Client>,
/// The context to pass to spawned filter programs.
pub context: gix_command::Context,
}
/// Initialization
impl State {
/// Create a new instance using `context` to inform launched processes about their environment.
pub fn new(context: gix_command::Context) -> Self {
Self {
running: Default::default(),
context,
}
}
}
impl Clone for State {
fn clone(&self) -> Self {
State {
running: Default::default(),
context: self.context.clone(),
}
}
}
/// A way to reference a running multi-file filter process for later acquisition of delayed output.
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Key(BString);
/// Substitute `path` as shell-save version into `cmd` which could be something like `cmd something %f`.
fn substitute_f_parameter(cmd: &BStr, path: &BStr) -> BString {
let mut buf: BString = Vec::with_capacity(cmd.len()).into();
let mut ofs = 0;
while let Some(pos) = cmd[ofs..].find(b"%f") {
buf.push_str(&cmd[..ofs + pos]);
buf.extend_from_slice(&gix_quote::single(path));
ofs += pos + 2;
}
buf.push_str(&cmd[ofs..]);
buf
}