mockforge_plugin_sdk/macros.rs
1//! Helper macros for plugin development
2
3/// Export a plugin with boilerplate
4///
5/// This macro generates the necessary WASM exports for your plugin.
6///
7/// # Example
8///
9/// ```rust,no_run
10/// use mockforge_plugin_sdk::{export_plugin, prelude::*, Result as PluginCoreResult};
11/// use std::collections::HashMap;
12///
13/// #[derive(Debug, Default)]
14/// pub struct MyPlugin;
15///
16/// #[async_trait]
17/// impl AuthPlugin for MyPlugin {
18/// fn capabilities(&self) -> PluginCapabilities {
19/// PluginCapabilities::default()
20/// }
21///
22/// async fn initialize(&self, _config: &AuthPluginConfig) -> PluginCoreResult<()> {
23/// Ok(())
24/// }
25///
26/// async fn authenticate(
27/// &self,
28/// _context: &PluginContext,
29/// _request: &AuthRequest,
30/// _config: &AuthPluginConfig,
31/// ) -> PluginCoreResult<PluginResult<AuthResponse>> {
32/// let identity = UserIdentity::new("user123");
33/// let response = AuthResponse::success(identity, HashMap::new());
34/// Ok(PluginResult::success(response, 0))
35/// }
36///
37/// fn validate_config(&self, _config: &AuthPluginConfig) -> PluginCoreResult<()> {
38/// Ok(())
39/// }
40///
41/// fn supported_schemes(&self) -> Vec<String> {
42/// vec!["basic".to_string()]
43/// }
44///
45/// async fn cleanup(&self) -> PluginCoreResult<()> {
46/// Ok(())
47/// }
48/// }
49///
50/// export_plugin!(MyPlugin);
51/// ```
52#[macro_export]
53macro_rules! export_plugin {
54 ($plugin_type:ty) => {
55 /// Create plugin instance
56 #[no_mangle]
57 pub extern "C" fn create_plugin() -> *mut std::ffi::c_void {
58 let plugin = Box::new(<$plugin_type>::default());
59 Box::into_raw(plugin) as *mut std::ffi::c_void
60 }
61
62 /// Destroy plugin instance
63 #[no_mangle]
64 pub extern "C" fn destroy_plugin(ptr: *mut std::ffi::c_void) {
65 if !ptr.is_null() {
66 unsafe {
67 let _ = Box::from_raw(ptr as *mut $plugin_type);
68 }
69 }
70 }
71 };
72}
73
74/// Generate a plugin configuration struct
75///
76/// # Example
77///
78/// ```rust,no_run
79/// use mockforge_plugin_sdk::plugin_config;
80///
81/// plugin_config! {
82/// id = "my-plugin",
83/// version = "1.0.0",
84/// name = "My Plugin",
85/// description = "A custom plugin",
86/// capabilities = ["network:http"],
87/// author = {
88/// name = "Your Name",
89/// email = "your.email@example.com",
90/// },
91/// }
92/// ```
93#[macro_export]
94macro_rules! plugin_config {
95 (
96 id = $id:expr,
97 version = $version:expr,
98 name = $name:expr,
99 description = $desc:expr,
100 capabilities = [$($capability:expr),*],
101 author = {
102 name = $author_name:expr,
103 email = $author_email:expr $(,)?
104 } $(,)?
105 ) => {
106 /// Plugin configuration
107 pub fn plugin_config() -> mockforge_plugin_core::PluginManifest {
108 use mockforge_plugin_core::*;
109
110 let info = PluginInfo::new(
111 PluginId::new($id),
112 PluginVersion::parse($version).expect("Invalid version"),
113 $name,
114 $desc,
115 PluginAuthor::with_email($author_name, $author_email),
116 );
117
118 let mut manifest = PluginManifest::new(info);
119 $(
120 manifest.capabilities.push($capability.to_string());
121 )*
122 manifest
123 }
124 };
125}
126
127/// Quick test macro for plugin functions
128///
129/// # Example
130///
131/// ```rust,no_run
132/// # use mockforge_plugin_sdk::prelude::*;
133/// # async fn test_auth() {
134/// use axum::http::{HeaderMap, Method, Uri};
135/// use mockforge_plugin_sdk::{
136/// mock_context, plugin_test, prelude::*, Result as PluginCoreResult,
137/// };
138/// use std::collections::HashMap;
139///
140/// #[derive(Default)]
141/// struct TestPlugin;
142///
143/// #[async_trait]
144/// impl AuthPlugin for TestPlugin {
145/// fn capabilities(&self) -> PluginCapabilities {
146/// PluginCapabilities::default()
147/// }
148///
149/// async fn initialize(&self, _config: &AuthPluginConfig) -> PluginCoreResult<()> {
150/// Ok(())
151/// }
152///
153/// async fn authenticate(
154/// &self,
155/// _context: &PluginContext,
156/// _request: &AuthRequest,
157/// _config: &AuthPluginConfig,
158/// ) -> PluginCoreResult<PluginResult<AuthResponse>> {
159/// let identity = UserIdentity::new("user");
160/// let response = AuthResponse::success(identity, HashMap::new());
161/// Ok(PluginResult::success(response, 0))
162/// }
163///
164/// fn validate_config(&self, _config: &AuthPluginConfig) -> PluginCoreResult<()> {
165/// Ok(())
166/// }
167///
168/// fn supported_schemes(&self) -> Vec<String> {
169/// vec!["basic".to_string()]
170/// }
171///
172/// async fn cleanup(&self) -> PluginCoreResult<()> {
173/// Ok(())
174/// }
175/// }
176///
177/// plugin_test! {
178/// test_name: authenticate_valid_user,
179/// plugin: TestPlugin,
180/// context: mock_context! {
181/// plugin_id: "test-plugin",
182/// request_id: "req-123",
183/// },
184/// request: AuthRequest::from_axum(
185/// Method::GET,
186/// Uri::from_static("/login"),
187/// HeaderMap::new(),
188/// None
189/// ),
190/// config: AuthPluginConfig::default(),
191/// assert: |result| {
192/// assert!(result.unwrap().is_success());
193/// }
194/// }
195/// # }
196/// ```
197#[macro_export]
198macro_rules! plugin_test {
199 (
200 test_name: $name:ident,
201 plugin: $plugin:ty,
202 context: $context:expr,
203 request: $request:expr,
204 config: $config:expr,
205 assert: $assert:expr
206 ) => {
207 #[tokio::test]
208 async fn $name() {
209 let plugin = <$plugin>::default();
210 let context = $context;
211 let request = $request;
212 let config = $config;
213 let result = plugin.authenticate(&context, &request, &config).await;
214 ($assert)(result);
215 }
216 };
217}
218
219/// Create a mock plugin context for testing
220///
221/// # Example
222///
223/// ```rust,no_run
224/// # use mockforge_plugin_sdk::{mock_context, prelude::*};
225/// let context = mock_context! {
226/// plugin_id: "test-plugin",
227/// request_id: "req-123",
228/// };
229/// ```
230#[macro_export]
231macro_rules! mock_context {
232 (
233 plugin_id: $plugin_id:expr,
234 request_id: $request_id:expr $(,)?
235 ) => {{
236 use mockforge_plugin_core::{PluginContext, PluginId, PluginVersion};
237 let mut context =
238 PluginContext::new(PluginId::new($plugin_id), PluginVersion::new(0, 1, 0));
239 context.request_id = $request_id.to_string();
240 context
241 }};
242
243 () => {{
244 use mockforge_plugin_core::{PluginContext, PluginId, PluginVersion};
245 PluginContext::new(PluginId::new("mockforge-plugin"), PluginVersion::new(0, 1, 0))
246 }};
247}
248
249#[cfg(test)]
250mod tests {
251
252 #[test]
253 fn test_macro_compilation() {
254 // Just verify macros compile
255 // Test passes if compilation succeeds
256 }
257}