Expand description
§pmpx-plugin
The pmpx plugin contract: one trait plus a stable C ABI that carries the trait safely across
the dlopen boundary.
A plugin author only implements PackageManager and then uses the one-line
export! to generate the whole C ABI shell:
pub fn create() -> Box<dyn PackageManager> { Box::new(CargoPlugin) }
pmpx_plugin::export!(create);§What a plugin may and may not do
PackageManager::command should only map from its inputs: it does not read files (including
anything under project_root), does not write files, does not read environment variables,
does not spawn child processes, and does not make network requests. That keeps command()
completely pure (unit tests need no fixture directory at all) and stops a plugin from using
file reads to probe things it should not know.
“What the project looks like” reaches a plugin through its manifest, in two declarative,
auditable forms: the file names in [detect], handed over as Context::matched, and the
file contents declared in [context] files, asked for one at a time through Context::file
(or Context::file_str). A plugin that needs to know what a lockfile pins, or whether
package.json mentions packageManager, declares that file and parses it itself; it never
reaches for the filesystem, and pmpx never learns what is inside.
Declaring a file is the allowlist, not a delivery: a name the manifest did not declare is never readable, and a file nobody asks for is never read.
§Saying something
A plugin that wants to explain itself calls debug! /
info! / warn! / error!,
and the host decides what to print, adding the plugin’s id. That is not only tidier than
printing directly: since the host holds the switch, --debug never becomes an input a plugin
could branch on, so a debug run executes the same command as any other. See
debug for the details, including what happens with no host installed (a
plugin’s own cargo test).
Data crossing the boundary is always #[repr(C)] POD, so the two sides need not share a rustc;
see the module docs of abi. A host asks the plugin for the capabilities it supports –
identity, command, and optionally attach – and passes a context whose every value is read
through an accessor, which is what lets either side gain a key or a capability without a new
contract version. shell is the plugin’s end of that; pmpx-loader is the host’s.
The contract’s parts live in sibling modules and are re-exported here, so every path that
starts with pmpx_plugin:: is stable: PackageManager and the Context it is called
with, the CommandSpec it answers with, the Verbs, the Family and the
PluginError it may report.
Re-exports§
pub use pmpx_plugin_abi as abi;
Modules§
- debug
- Saying something to the person running pmpx.
- shell
- The plugin side of the ABI: everything between
export!’s shims and the trait a plugin writes.
Macros§
- debug
- Say something for whoever is debugging.
- error
- Say something that went wrong.
- export
- Generate the C shell for one
PackageManagerimplementation. - info
- Say what the plugin decided, in one line.
- warn
- Say that something is off, but the command still runs.
Structs§
- Command
Spec - One command waiting to be executed, pure data – a plugin only describes “what to run” and the
actual spawn is done by the host, so stdio, environment, and exit-code handling have exactly
one implementation. Construction is a consuming chain (
self->Self), so it can produce a value directly: - Context
- The context the host passes to the plugin. Read-only.
- Context
Builder - Assemble a context by hand, for a plugin’s own tests.
- Context
File - The contents of one file a plugin asked to see.
- Family
- Ecosystem family. Decides the grouping headers of the host’s
plugin ls, the scope ofplugin set, and the key names of[plugin] <family> = "..."in a project’s.pmpx.toml. An open type rather than a closed enum: known ecosystems have constants, unknown ones extend viaFamily::new, and comparison and ordering go by string – a third-party plugin supporting a new ecosystem needs neither a change to this crate nor a host release.
Enums§
- Plugin
Error - The errors a plugin can report. Deliberately only three – because the host only needs to distinguish three: the verb is not supported (so it can degrade to passing through verbatim), the arguments are wrong, and everything else. The human-readable description goes in the payload and the host prints it to stderr as-is.
- Selection
Reason - Why the host selected this plugin.
- Verb
- The verbs pmpx recognizes. A closed set – this is all the command line has.
Traits§
- Package
Manager - An implementation of one package manager backend, a purely synchronous interface: no
async, no callbacks, no I/O – passing aFutureacross thedlopenboundary is the most fragile part of this approach. It should only do mapping; see the crate docs for the constraints.