Skip to main content

lumen_scene/
compiler_plugins.rs

1//! Injected compiler-plugin boundary.
2//!
3//! Compiler plugins are loaded and driven by the compiler (`lumenc`), but the
4//! compile pipeline they hook into (`load_ir`, `compile_app`, hot reload)
5//! lives in the runtime crate, which links no plugin loader for the same
6//! reason it links no parser (see [`crate::source_parser`]): the dependency
7//! would run the wrong way. The chain is injected instead:
8//!
9//! - The CLI, the Rust SDK, and the C-ABI dev paths hand a
10//!   [`CompilerPlugins`] to `RunOptions`, built by `lumenc` from the app's
11//!   `[[plugins]]` declarations.
12//! - The dev source-load path and hot reload call it.
13//! - The precompiled-artifact path already carries the transformed tree and
14//!   ignores this hook.
15//!
16//! Errors are `String`s that already name the failing plugin; a hook error
17//! fails the compile with it.
18
19use bevy_ecs::prelude::Resource;
20use lumen_ir::layout_ir::LayoutIR;
21use std::path::Path;
22use std::sync::Arc;
23
24/// The loaded compiler-plugin chain of one app. Implemented in `lumenc` over
25/// the `lumenc-plugin` loader; every hook runs the declared plugins in
26/// `lumen.toml` order.
27pub trait CompilerPlugins: Send + Sync {
28    /// Rewrite the entry markup text, before `<include>` splicing.
29    fn transform_markup(&self, src: String, entry: &Path) -> Result<String, String>;
30    /// Rewrite the entry stylesheet text, before `@import` splicing.
31    fn transform_css(&self, src: String, entry: &Path, file: &Path) -> Result<String, String>;
32    /// Transform the assembled tree, before asset resolution and the cascade.
33    fn transform_ir(&self, ir: &mut LayoutIR, entry: &Path) -> Result<(), String>;
34    /// Lint the cascaded tree and write emit outputs. Returns rendered
35    /// diagnostic lines for the caller to print beside the built-in lint
36    /// findings.
37    fn finish(&self, ir: &LayoutIR, entry: &Path) -> Result<Vec<String>, String>;
38}
39
40/// The empty chain: every hook is a no-op. What every path without a
41/// `[[plugins]]` declaration runs.
42pub struct NoCompilerPlugins;
43
44impl CompilerPlugins for NoCompilerPlugins {
45    fn transform_markup(&self, src: String, _entry: &Path) -> Result<String, String> {
46        Ok(src)
47    }
48    fn transform_css(&self, src: String, _entry: &Path, _file: &Path) -> Result<String, String> {
49        Ok(src)
50    }
51    fn transform_ir(&self, _ir: &mut LayoutIR, _entry: &Path) -> Result<(), String> {
52        Ok(())
53    }
54    fn finish(&self, _ir: &LayoutIR, _entry: &Path) -> Result<Vec<String>, String> {
55        Ok(Vec::new())
56    }
57}
58
59/// The injected chain, stashed as a resource beside
60/// [`crate::source_parser::RuntimeParser`] so a hot reload reruns the same
61/// hooks without reloading the plugin libraries.
62#[derive(Resource, Clone)]
63pub struct RuntimeCompilerPlugins(pub Arc<dyn CompilerPlugins>);