Skip to main content

hm_plugin_sdk/
macros.rs

1//! The `register_plugin!` macro generates the Extism plugin entry
2//! points from a plugin's manifest and capability impls.
3//!
4//! A plugin can pass zero or more of `subcommand`, `executor`, `hook`,
5//! `output` to register concrete implementations, in any order. Any
6//! capability the plugin declares in its manifest but does not register
7//! here is a compile-time omission — the host will call into an
8//! unimplemented export at runtime and fail loudly.
9//!
10//! Two capability entries of the same kind (e.g. `executor = A,
11//! executor = B`) are not detected by the macro itself, but each kind
12//! emits a uniquely-named extern fn (`hm_executor_run`, etc.). Two of
13//! the same kind therefore fails at type-check with a clean
14//! "duplicate definition" error from rustc.
15
16/// Generate `hm_manifest` + capability exports for a plugin.
17///
18/// # Example
19///
20/// ```ignore
21/// register_plugin!(
22///     manifest = ...,
23///     executor = MyExec,
24///     hook     = MyHook,
25/// );
26///
27/// // Order-independent: this is equivalent.
28/// register_plugin!(
29///     manifest = ...,
30///     hook     = MyHook,
31///     executor = MyExec,
32/// );
33/// ```
34#[macro_export]
35macro_rules! register_plugin {
36    (manifest = $manifest:expr $(, $($tail:tt)*)?) => {
37        #[$crate::extism_pdk::plugin_fn]
38        pub fn hm_manifest(_: ()) -> $crate::extism_pdk::FnResult<$crate::extism_pdk::Json<$crate::PluginManifest>> {
39            Ok($crate::extism_pdk::Json($manifest))
40        }
41
42        $crate::__rp_dispatch!($($($tail)*)?);
43    };
44}
45
46/// Dispatch loop for capability impls. Consumes one `key = $ty` pair
47/// at a time and recurses on the tail. Order-independent because every
48/// arm matches by keyword.
49#[macro_export]
50#[doc(hidden)]
51macro_rules! __rp_dispatch {
52    // Base case: nothing left (with or without trailing comma).
53    () => {};
54    (,) => {};
55
56    (subcommand = $ty:ty $(, $($rest:tt)*)?) => {
57        #[$crate::extism_pdk::plugin_fn]
58        pub fn hm_subcommand_run(
59            $crate::extism_pdk::Json(input): $crate::extism_pdk::Json<$crate::SubcommandInput>,
60        ) -> $crate::extism_pdk::FnResult<$crate::extism_pdk::Json<$crate::ExitInfo>> {
61            let plugin = <$ty as ::core::default::Default>::default();
62            match $crate::SubcommandPlugin::run(&plugin, input) {
63                Ok(info) => Ok($crate::extism_pdk::Json(info)),
64                Err(e) => Err($crate::extism_pdk::WithReturnCode::new(e.into(), 1)),
65            }
66        }
67        $crate::__rp_dispatch!($($($rest)*)?);
68    };
69
70    (executor = $ty:ty $(, $($rest:tt)*)?) => {
71        #[$crate::extism_pdk::plugin_fn]
72        pub fn hm_executor_run(
73            $crate::extism_pdk::Json(input): $crate::extism_pdk::Json<$crate::ExecutorInput>,
74        ) -> $crate::extism_pdk::FnResult<$crate::extism_pdk::Json<$crate::StepResult>> {
75            let plugin = <$ty as ::core::default::Default>::default();
76            match $crate::StepExecutor::run(&plugin, input) {
77                Ok(r) => Ok($crate::extism_pdk::Json(r)),
78                Err(e) => Err($crate::extism_pdk::WithReturnCode::new(e.into(), 1)),
79            }
80        }
81        $crate::__rp_dispatch!($($($rest)*)?);
82    };
83
84    (hook = $ty:ty $(, $($rest:tt)*)?) => {
85        #[$crate::extism_pdk::plugin_fn]
86        pub fn hm_hook_on_event(
87            $crate::extism_pdk::Json(event): $crate::extism_pdk::Json<$crate::HookEvent>,
88        ) -> $crate::extism_pdk::FnResult<$crate::extism_pdk::Json<$crate::HookOutcome>> {
89            let plugin = <$ty as ::core::default::Default>::default();
90            match $crate::LifecycleHook::on_event(&plugin, event) {
91                Ok(o) => Ok($crate::extism_pdk::Json(o)),
92                Err(e) => Err($crate::extism_pdk::WithReturnCode::new(e.into(), 1)),
93            }
94        }
95        $crate::__rp_dispatch!($($($rest)*)?);
96    };
97
98    (output = $ty:ty $(, $($rest:tt)*)?) => {
99        #[$crate::extism_pdk::plugin_fn]
100        pub fn hm_output_on_event(
101            $crate::extism_pdk::Json(event): $crate::extism_pdk::Json<$crate::BuildEvent>,
102        ) -> $crate::extism_pdk::FnResult<()> {
103            let plugin = <$ty as ::core::default::Default>::default();
104            $crate::OutputFormatter::on_event(&plugin, event)
105                .map_err(|e| $crate::extism_pdk::WithReturnCode::new(e.into(), 1))?;
106            Ok(())
107        }
108
109        #[$crate::extism_pdk::plugin_fn]
110        pub fn hm_output_finalize(_: ()) -> $crate::extism_pdk::FnResult<Vec<u8>> {
111            let plugin = <$ty as ::core::default::Default>::default();
112            $crate::OutputFormatter::finalize(&plugin)
113                .map_err(|e| $crate::extism_pdk::WithReturnCode::new(e.into(), 1))
114        }
115        $crate::__rp_dispatch!($($($rest)*)?);
116    };
117}