pmpx_plugin/spec.rs
1//! What a plugin answers with: one command, still unexecuted.
2
3use std::ffi::OsString;
4use std::path::PathBuf;
5
6/// One command waiting to be executed, pure data -- a plugin only describes "what to run" and the
7/// actual spawn is done by the host, so stdio, environment, and exit-code handling have exactly
8/// one implementation. Construction is a consuming chain (`self` -> `Self`), so it can produce a
9/// value directly:
10/// ```ignore
11/// let spec = CommandSpec::new("cargo").arg("add").args(args.iter()).cwd("/somewhere");
12/// ```
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct CommandSpec {
15 /// Executable. The host resolves it to a real path with `which` and decides per platform
16 /// whether to wrap it in `cmd /C` (on Windows `pnpm` is really `pnpm.cmd`, and spawning it
17 /// directly fails).
18 pub program: OsString,
19
20 /// Arguments, in order.
21 pub args: Vec<OsString>,
22
23 /// Working-directory override. `None` = use the project root given by the host.
24 pub cwd: Option<PathBuf>,
25}
26
27impl CommandSpec {
28 /// Specify the executable.
29 pub fn new(program: impl Into<OsString>) -> Self {
30 Self {
31 program: program.into(),
32 args: Vec::new(),
33 cwd: None,
34 }
35 }
36
37 /// Append one argument.
38 pub fn arg(mut self, arg: impl Into<OsString>) -> Self {
39 self.args.push(arg.into());
40 self
41 }
42
43 /// Append a batch of arguments; `args.iter()` can be passed straight in and stays lossless
44 /// (`&OsString: Into<OsString>`), unlike `String`, which would corrupt non-UTF-8 arguments on
45 /// Unix.
46 pub fn args<I, S>(mut self, args: I) -> Self
47 where
48 I: IntoIterator<Item = S>,
49 S: Into<OsString>,
50 {
51 self.args.extend(args.into_iter().map(Into::into));
52 self
53 }
54
55 /// Override the working directory.
56 pub fn cwd(mut self, dir: impl Into<PathBuf>) -> Self {
57 self.cwd = Some(dir.into());
58 self
59 }
60}