vtx_sdk/plugin/
mod.rs

1//! Low-boilerplate plugin exports.
2
3use crate::error::{VtxError, VtxResult};
4use crate::modules::event::PluginEvent;
5use crate::modules::net::http::{Request, Response, ResponseBuilder};
6use crate::{Capabilities, Manifest, UserContext};
7
8/// A high-level trait for defining Vtx plugins with minimal boilerplate.
9///
10/// The `bindings::Guest` trait generated by `wit-bindgen` requires implementing all exported functions.
11/// This trait provides default implementations for common methods and allows returning `VtxResult`
12/// directly in `handle` and `handle_event` for better error handling ergonomics.
13pub trait VtxPlugin {
14    fn handle(_req: Request) -> VtxResult<Response> {
15        Ok(ResponseBuilder::not_found())
16    }
17
18    fn handle_event(_event: PluginEvent) -> VtxResult<()> {
19        Ok(())
20    }
21
22    fn get_migrations() -> Vec<String> {
23        Vec::new()
24    }
25
26    fn get_manifest() -> Manifest;
27
28    fn get_resources() -> Vec<String> {
29        Vec::new()
30    }
31
32    fn get_capabilities() -> Capabilities;
33
34    /// Performs authentication based on request headers.
35    ///
36    /// By default, this returns 401 (Unauthorized), indicating that this plugin does not
37    /// handle authentication. This design ensures it does not block other plugins in the
38    /// chain of responsibility that might handle authentication.
39    fn authenticate(_headers: &[(String, String)]) -> VtxResult<UserContext> {
40        Err(VtxError::AuthDenied(401))
41    }
42}
43
44/// Exports a type implementing `VtxPlugin` as the `world vtx-plugin` Guest for WIT.
45///
46/// Usage:
47/// - `export_plugin!(MyPlugin)`: Generates a default guest adapter and exports it.
48/// - `export_plugin!(MyPlugin => MyGuest)`: Defines a custom adapter name (useful to avoid naming
49///   conflicts when the macro is called multiple times within the same crate).
50#[macro_export]
51macro_rules! export_plugin {
52    ($plugin:ty) => {
53        $crate::export_plugin!($plugin => __VtxSdkGuest);
54    };
55    ($plugin:ty => $guest:ident) => {
56        struct $guest;
57
58        impl $crate::bindings::Guest for $guest {
59            fn handle(req: $crate::http::Request) -> $crate::http::Response {
60                match <$plugin as $crate::plugin::VtxPlugin>::handle(req) {
61                    Ok(resp) => resp,
62                    Err(err) => $crate::http::ResponseBuilder::error(err),
63                }
64            }
65
66            fn handle_event(event: $crate::event::PluginEvent) -> Result<(), String> {
67                match <$plugin as $crate::plugin::VtxPlugin>::handle_event(event) {
68                    Ok(()) => Ok(()),
69                    Err(err) => Err(err.to_string()),
70                }
71            }
72
73            fn get_migrations() -> Vec<String> {
74                <$plugin as $crate::plugin::VtxPlugin>::get_migrations()
75            }
76
77            fn get_manifest() -> $crate::Manifest {
78                <$plugin as $crate::plugin::VtxPlugin>::get_manifest()
79            }
80
81            fn get_resources() -> Vec<String> {
82                <$plugin as $crate::plugin::VtxPlugin>::get_resources()
83            }
84
85            fn get_capabilities() -> $crate::Capabilities {
86                <$plugin as $crate::plugin::VtxPlugin>::get_capabilities()
87            }
88
89            fn authenticate(
90                headers: Vec<(String, String)>,
91            ) -> Result<$crate::UserContext, u16> {
92                use $crate::auth::IntoAuthResult as _;
93                <$plugin as $crate::plugin::VtxPlugin>::authenticate(&headers).into_auth_result()
94            }
95        }
96
97        $crate::export!($guest);
98    };
99}