Skip to main content

Crate pmpx_plugin

Crate pmpx_plugin 

Source
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 PackageManager implementation.
info
Say what the plugin decided, in one line.
warn
Say that something is off, but the command still runs.

Structs§

CommandSpec
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.
ContextBuilder
Assemble a context by hand, for a plugin’s own tests.
ContextFile
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 of plugin 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 via Family::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§

PluginError
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.
SelectionReason
Why the host selected this plugin.
Verb
The verbs pmpx recognizes. A closed set – this is all the command line has.

Traits§

PackageManager
An implementation of one package manager backend, a purely synchronous interface: no async, no callbacks, no I/O – passing a Future across the dlopen boundary is the most fragile part of this approach. It should only do mapping; see the crate docs for the constraints.