linesmith_core/plugins/mod.rs
1//! Plugin runtime bridge layer.
2//!
3//! The rhai-pure plugin host (engine construction, discovery,
4//! registry, `@data_deps` header parsing, error types) lives in
5//! [`linesmith_plugin`] per ADR-0018 / ADR-0020 — consumers reach
6//! those types from that crate directly, not through this module.
7//! What lives here is the consumer-side bridge: the
8//! [`RhaiSegment`] adapter that lets compiled plugins implement the
9//! `Segment` trait, the [`build_ctx`] `DataContext` → `rhai::Map`
10//! mirror, [`validate_return`] which decodes a plugin's `rhai::Map`
11//! into a [`crate::segments::RenderedSegment`], and the
12//! [`build_engine`] wrapper that installs a `LINESMITH_LOG`-respecting
13//! warn emitter before delegating to [`linesmith_plugin::build_engine`].
14
15pub mod ctx_mirror;
16pub mod output;
17pub mod segment;
18
19pub use ctx_mirror::build_ctx;
20pub use output::validate_return;
21pub use segment::RhaiSegment;
22
23/// Build the rhai plugin engine with linesmith-core's logger wired
24/// as the host's warn emitter, so plugin `log()` output respects
25/// `LINESMITH_LOG`. Wraps [`linesmith_plugin::build_engine`].
26///
27/// Every entry point that builds a plugin engine via linesmith-core
28/// (CLI driver, library `run` / `run_with_*` family, doctor,
29/// `runtime::plugins::load_plugins`) installs the emitter before
30/// the first render. Direct consumers of
31/// [`linesmith_plugin::build_engine`] skip this bridge by design —
32/// that's the documented entry point for embedders who don't want
33/// linesmith-core's logger.
34#[must_use]
35pub fn build_engine() -> std::sync::Arc<linesmith_plugin::rhai::Engine> {
36 install_plugin_warn_emitter();
37 // The wrapper IS the documented bypass-target; calling the bare
38 // constructor here is the one legitimate use of the disallowed
39 // method (every other consumer should reach for this wrapper).
40 #[allow(clippy::disallowed_methods)]
41 linesmith_plugin::build_engine()
42}
43
44/// One-shot install that bridges plugin `log()` output through
45/// `crate::logging::emit` so `LINESMITH_LOG` gates plugin
46/// diagnostics. Called from [`build_engine`] so every entry point
47/// that builds an engine via this crate picks up the bridge before
48/// the first render.
49fn install_plugin_warn_emitter() {
50 static ONCE: std::sync::Once = std::sync::Once::new();
51 ONCE.call_once(|| {
52 linesmith_plugin::engine::install_warn_emitter(Box::new(|msg| {
53 crate::logging::emit(crate::logging::Level::Warn, msg);
54 }));
55 });
56}