lattice_mode/plugin_meta_sink.rs
1//! `PluginMetaSink` — the generic seam the plugin loader writes plugin
2//! provenance through (PL8.B).
3//!
4//! The host owns a `PluginMetaRegistry` (host-issued `PluginId` → manifest name +
5//! doc) that backs provenance display (`SourceLayer::Plugin(id)` renders as
6//! the manifest name) and the loaded-plugin introspection surfaces
7//! (`:list-plugins` / `:describe-plugin`). The plugin loader
8//! (`lattice-plugin-loader`) must populate it as each plugin loads — but the
9//! loader cannot name the host's `PluginMetaRegistry` type without a dependency
10//! cycle. So the host implements this trait over its registry and registers the
11//! same instance as a [`PluginMetaSinkHandle`] service; the loader looks the
12//! handle up ([`SubsystemBoot::service`](crate::SubsystemBoot::service)) and
13//! writes through it. Mirrors the `PluginEventSink` type-erasure pattern that
14//! keeps `lattice-runtime` free of a plugin-host dep.
15
16use std::sync::Arc;
17
18/// The host-owned plugin-metadata store, viewed as a write seam. Implemented by
19/// the host's `PluginMetaRegistry`; consumed by the plugin loader. All methods
20/// take `&self` (the store is interior-mutable behind a lock) so the shared
21/// `Arc` handle suffices — no `&mut` plumbing across the boundary.
22pub trait PluginMetaSink: Send + Sync {
23 /// Record a loaded plugin's manifest name + doc against its host-issued
24 /// numeric id, so `SourceLayer::Plugin(id)` provenance renders as the name
25 /// and `:list-plugins` shows it. Called once per plugin at load.
26 fn register_plugin(&self, id: u32, name: String, doc: String);
27
28 /// Record that `seam_ids` all belong to the plugin registered as `primary`.
29 ///
30 /// A plugin is instantiated once per seam and each instance gets its own
31 /// host id; `primary` (the first) is its identity, but a contribution is
32 /// stamped with the id of the seam that made it. Without this, a binding
33 /// the `keymap` seam registered renders as `<plugin:29>` while the same
34 /// plugin's grammar renders as its name. Aliases only — `:list-plugins`
35 /// must still show the plugin once. `unregister_plugin(primary)` drops
36 /// them.
37 ///
38 /// Defaults to a no-op so a test sink need not care; the host's registry
39 /// is the one implementation that must.
40 fn register_seam_ids(&self, primary: u32, seam_ids: &[u32]) {
41 let _ = (primary, seam_ids);
42 }
43
44 /// Forget a plugin's metadata (unload / reload). Idempotent — a
45 /// never-registered or already-removed id is a no-op.
46 fn unregister_plugin(&self, id: u32);
47}
48
49/// The service alias the host registers and the loader looks up. Per the
50/// `ServiceRegistry` Arc/TypeId rule, register and look up with this exact type.
51pub type PluginMetaSinkHandle = Arc<dyn PluginMetaSink>;