Skip to main content

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. It may be a bare name (resolved on `PATH`), an absolute path, or a path
16    /// relative to this spec's [`cwd`](CommandSpec::cwd) -- which is the project root when the
17    /// spec does not name one. On Windows a bare name resolves through `PATHEXT`, so `pnpm` finds
18    /// `pnpm.cmd`, and the host starts a batch file the way the platform requires.
19    pub program: OsString,
20
21    /// Arguments, in order.
22    pub args: Vec<OsString>,
23
24    /// Working-directory override. `None` = use the project root given by the host.
25    pub cwd: Option<PathBuf>,
26}
27
28impl CommandSpec {
29    /// Specify the executable.
30    pub fn new(program: impl Into<OsString>) -> Self {
31        Self {
32            program: program.into(),
33            args: Vec::new(),
34            cwd: None,
35        }
36    }
37
38    /// Append one argument.
39    pub fn arg(mut self, arg: impl Into<OsString>) -> Self {
40        self.args.push(arg.into());
41        self
42    }
43
44    /// Append a batch of arguments; `args.iter()` can be passed straight in and stays lossless
45    /// (`&OsString: Into<OsString>`), unlike `String`, which would corrupt non-UTF-8 arguments on
46    /// Unix.
47    pub fn args<I, S>(mut self, args: I) -> Self
48    where
49        I: IntoIterator<Item = S>,
50        S: Into<OsString>,
51    {
52        self.args.extend(args.into_iter().map(Into::into));
53        self
54    }
55
56    /// Override the working directory.
57    pub fn cwd(mut self, dir: impl Into<PathBuf>) -> Self {
58        self.cwd = Some(dir.into());
59        self
60    }
61}