1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//! # 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!`](macro@crate::export) to generate the whole C ABI shell:
//!
//! ```ignore
//! 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!`](macro@crate::debug) /
//! [`info!`](macro@crate::info) / [`warn!`](macro@crate::warn) / [`error!`](macro@crate::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`](mod@crate::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 [`Verb`]s, the [`Family`] and the
//! [`PluginError`] it may report.
/// The wire format, for a plugin that needs to talk to it directly (a hand-written shell, a test).
pub use pmpx_plugin_abi as abi;
pub use ;
pub use PluginError;
pub use Family;
pub use PackageManager;
pub use CommandSpec;
pub use Verb;