Skip to content

Commit 38a0d9a

Browse files
authored
Merge pull request #1813 from EliahKagan/prepare-doc
Revise and somewhat expand `Prepare` docs
2 parents f3dc83b + 4e7306e commit 38a0d9a

File tree

1 file changed

+80
-41
lines changed

1 file changed

+80
-41
lines changed

gix-command/src/lib.rs

+80-41
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ use std::{
1010

1111
use bstr::{BString, ByteSlice};
1212

13-
/// A structure to keep settings to use when invoking a command via [`spawn()`][Prepare::spawn()], after creating it with [`prepare()`].
13+
/// A structure to keep settings to use when invoking a command via [`spawn()`][Prepare::spawn()],
14+
/// after creating it with [`prepare()`].
1415
pub struct Prepare {
15-
/// The command to invoke (either with or without shell depending on `use_shell`.
16+
/// The command to invoke, either directly or with a shell depending on `use_shell`.
1617
pub command: OsString,
1718
/// Additional information to be passed to the spawned command.
1819
pub context: Option<Context>,
@@ -22,29 +23,29 @@ pub struct Prepare {
2223
pub stdout: std::process::Stdio,
2324
/// The way standard error is configured.
2425
pub stderr: std::process::Stdio,
25-
/// The arguments to pass to the spawned process.
26+
/// The arguments to pass to the process being spawned.
2627
pub args: Vec<OsString>,
27-
/// environment variables to set in the spawned process.
28+
/// Environment variables to set for the spawned process.
2829
pub env: Vec<(OsString, OsString)>,
2930
/// If `true`, we will use `shell_program` or `sh` to execute the `command`.
3031
pub use_shell: bool,
31-
/// If `true`, `command` is assumed to be a command or path to the program to execute, and it will be shell-quoted
32-
/// to assure it will be executed as is and without splitting across whitespace.
32+
/// If `true`, `command` is assumed to be a command or path to the program to execute, and it
33+
/// will be shell-quoted to assure it will be executed as is and without splitting across
34+
/// whitespace.
3335
pub quote_command: bool,
3436
/// The name or path to the shell program to use instead of `sh`.
3537
pub shell_program: Option<OsString>,
36-
/// If `true` (default `true` on windows and `false` everywhere else)
37-
/// we will see if it's safe to manually invoke `command` after splitting
38-
/// its arguments as a shell would do.
39-
/// Note that outside of windows, it's generally not advisable as this
40-
/// removes support for literal shell scripts with shell-builtins.
38+
/// If `true` (default `true` on Windows and `false` everywhere else) we will see if it's safe
39+
/// to manually invoke `command` after splitting its arguments as a shell would do.
4140
///
42-
/// This mimics the behaviour we see with `git` on windows, which also
43-
/// won't invoke the shell there at all.
41+
/// Note that outside of Windows, it's generally not advisable as this removes support for
42+
/// literal shell scripts with shell-builtins.
4443
///
45-
/// Only effective if `use_shell` is `true` as well, as the shell will
46-
/// be used as a fallback if it's not possible to split arguments as
47-
/// the command-line contains 'scripting'.
44+
/// This mimics the behaviour we see with `git` on Windows, which also won't invoke the shell
45+
/// there at all.
46+
///
47+
/// Only effective if `use_shell` is `true` as well, as the shell will be used as a fallback if
48+
/// it's not possible to split arguments as the command-line contains 'scripting'.
4849
pub allow_manual_arg_splitting: bool,
4950
}
5051

@@ -96,81 +97,119 @@ mod prepare {
9697

9798
/// Builder
9899
impl Prepare {
99-
/// If called, the command will be checked for characters that are typical for shell scripts, and if found will use `sh` to execute it
100-
/// or whatever is set as [`with_shell_program()`](Self::with_shell_program()).
100+
/// If called, the command will be checked for characters that are typical for shell
101+
/// scripts, and if found will use `sh` to execute it or whatever is set as
102+
/// [`with_shell_program()`](Self::with_shell_program()).
103+
///
101104
/// If the command isn't valid UTF-8, a shell will always be used.
102105
///
103-
/// If a shell is used, then arguments given here with [arg()](Self::arg) or [args()](Self::args) will be substituted via `"$@"` if it's not
104-
/// already present in the command.
106+
/// If a shell is used, then arguments given here with [arg()](Self::arg) or
107+
/// [args()](Self::args) will be substituted via `"$@"` if it's not already present in the
108+
/// command.
109+
///
105110
///
106-
/// If this method is not called, commands are always executed verbatim, without the use of a shell.
111+
/// The [`command_may_be_shell_script_allow_manual_argument_splitting()`](Self::command_may_be_shell_script_allow_manual_argument_splitting())
112+
/// and [`command_may_be_shell_script_disallow_manual_argument_splitting()`](Self::command_may_be_shell_script_disallow_manual_argument_splitting())
113+
/// methods also call this method.
114+
///
115+
/// If neither this method nor [`with_shell()`](Self::with_shell()) is called, commands are
116+
/// always executed verbatim and directly, without the use of a shell.
107117
pub fn command_may_be_shell_script(mut self) -> Self {
108118
self.use_shell = self.command.to_str().map_or(true, |cmd| {
109119
cmd.as_bytes().find_byteset(b"|&;<>()$`\\\"' \t\n*?[#~=%").is_some()
110120
});
111121
self
112122
}
113123

114-
/// If called, unconditionally use a shell to execute the command and its arguments, and `sh` to execute it,
115-
/// or whatever is set as [`with_shell_program()`](Self::with_shell_program()).
124+
/// If called, unconditionally use a shell to execute the command and its arguments.
125+
///
126+
/// This uses `sh` to execute it, or whatever is set as
127+
/// [`with_shell_program()`](Self::with_shell_program()).
116128
///
117-
/// If a shell is used, then arguments given here with [arg()](Self::arg) or [args()](Self::args) will be substituted via `"$@"` if it's not
118-
/// already present in the command.
129+
/// Arguments given here with [arg()](Self::arg) or [args()](Self::args) will be
130+
/// substituted via `"$@"` if it's not already present in the command.
119131
///
120-
/// If this method is not called, commands are always executed verbatim, without the use of a shell.
132+
/// If neither this method nor
133+
/// [`command_may_be_shell_script()`](Self::command_may_be_shell_script()) is called,
134+
/// commands are always executed verbatim and directly, without the use of a shell. (But
135+
/// see [`command_may_be_shell_script()`](Self::command_may_be_shell_script()) on other
136+
/// methods that call that method.)
121137
pub fn with_shell(mut self) -> Self {
122138
self.use_shell = true;
123139
self
124140
}
125141

126-
/// If [`with_shell()`](Self::with_shell()) is set, then quote the command to assure its path is left intact.
142+
/// Quote the command if it is run in a shell, so its path is left intact.
127143
///
128-
/// Note that this should not be used if the command is a script - quoting is only the right choice if it's known to be a program path.
144+
/// This is only meaningful if the command has been arranged to run in a shell, either
145+
/// unconditionally with [`with_shell()`](Self::with_shell()), or conditionally with
146+
/// [`command_may_be_shell_script()`](Self::command_may_be_shell_script()).
147+
///
148+
/// Note that this should not be used if the command is a script - quoting is only the
149+
/// right choice if it's known to be a program path.
150+
///
151+
/// Note also that this does not affect arguments passed with [arg()](Self::arg) or
152+
/// [args()](Self::args), which do not have to be quoted by the *caller* because they are
153+
/// passed as `"$@"` positional parameters (`"$1"`, `"$2"`, and so on).
129154
pub fn with_quoted_command(mut self) -> Self {
130155
self.quote_command = true;
131156
self
132157
}
133158

134-
/// Set the name or path to the shell `program` to use if a shell is to be used, to avoid using the default shell which is `sh`.
159+
/// Set the name or path to the shell `program` to use if a shell is to be used, to avoid
160+
/// using the default shell which is `sh`.
161+
///
162+
/// Note that that shells that are not Bourne-style cannot be expected to work correctly,
163+
/// because POSIX shell syntax is assumed when searching for and conditionally adding
164+
/// `"$@"` to receive arguments, where applicable (and in the behaviour of
165+
/// [`with_quoted_command()`](Self::with_quoted_command()), if called).
135166
pub fn with_shell_program(mut self, program: impl Into<OsString>) -> Self {
136167
self.shell_program = Some(program.into());
137168
self
138169
}
139170

140171
/// Unconditionally turn off using the shell when spawning the command.
141-
/// Note that not using the shell is the default so an effective use of this method
142-
/// is some time after [`command_may_be_shell_script()`](Prepare::command_may_be_shell_script()) was called.
172+
///
173+
/// Note that not using the shell is the default. So an effective use of this method
174+
/// is some time after [`command_may_be_shell_script()`](Self::command_may_be_shell_script())
175+
/// or [`with_shell()`](Self::with_shell()) was called.
143176
pub fn without_shell(mut self) -> Self {
144177
self.use_shell = false;
145178
self
146179
}
147180

148181
/// Set additional `ctx` to be used when spawning the process.
149182
///
150-
/// Note that this is a must for most kind of commands that `git` usually spawns,
151-
/// as at least they need to know the correct `git` repository to function.
183+
/// Note that this is a must for most kind of commands that `git` usually spawns, as at
184+
/// least they need to know the correct Git repository to function.
152185
pub fn with_context(mut self, ctx: Context) -> Self {
153186
self.context = Some(ctx);
154187
self
155188
}
156189

157-
/// Like [`command_may_be_shell_script()`](Prepare::command_may_be_shell_script()), but try to split arguments by hand if this can be safely done without a shell.
190+
/// Like [`command_may_be_shell_script()`](Self::command_may_be_shell_script()), but try to
191+
/// split arguments by hand if this can be safely done without a shell.
192+
///
193+
/// This is useful on platforms where spawning processes is slow, or where many processes
194+
/// have to be spawned in a row which should be sped up. Manual argument splitting is
195+
/// enabled by default on Windows only.
158196
///
159-
/// This is useful on platforms where spawning processes is slow, or where many processes have to be spawned in a raw which should be sped up.
160-
/// Manual argument splitting is enabled by default on Windows only.
197+
/// Note that this does *not* check for the use of possible shell builtins. Commands may
198+
/// fail or behave differently if they are available as shell builtins and no corresponding
199+
/// external command exists, or the external command behaves differently.
161200
pub fn command_may_be_shell_script_allow_manual_argument_splitting(mut self) -> Self {
162201
self.allow_manual_arg_splitting = true;
163202
self.command_may_be_shell_script()
164203
}
165204

166-
/// Like [`command_may_be_shell_script()`](Prepare::command_may_be_shell_script()), but don't allow to bypass the shell even if manual argument splitting
167-
/// can be performed safely.
205+
/// Like [`command_may_be_shell_script()`](Self::command_may_be_shell_script()), but don't
206+
/// allow to bypass the shell even if manual argument splitting can be performed safely.
168207
pub fn command_may_be_shell_script_disallow_manual_argument_splitting(mut self) -> Self {
169208
self.allow_manual_arg_splitting = false;
170209
self.command_may_be_shell_script()
171210
}
172211

173-
/// Configure the process to use `stdio` for _stdin.
212+
/// Configure the process to use `stdio` for _stdin_.
174213
pub fn stdin(mut self, stdio: Stdio) -> Self {
175214
self.stdin = stdio;
176215
self
@@ -180,7 +219,7 @@ mod prepare {
180219
self.stdout = stdio;
181220
self
182221
}
183-
/// Configure the process to use `stdio` for _stderr.
222+
/// Configure the process to use `stdio` for _stderr_.
184223
pub fn stderr(mut self, stdio: Stdio) -> Self {
185224
self.stderr = stdio;
186225
self
@@ -256,7 +295,7 @@ mod prepare {
256295
prep.command.push(" \"$@\"");
257296
} else {
258297
gix_trace::debug!(
259-
"Will not add '$@' to '{:?}' as it seems to contain it already",
298+
"Will not add '\"$@\"' to '{:?}' as it seems to contain '$@' already",
260299
prep.command
261300
);
262301
}

0 commit comments

Comments
 (0)