nautilus_plugin/
macros.rs1#[macro_export]
32macro_rules! nautilus_plugin {
33 (
34 $(name: $name:expr,)?
35 $(vendor: $vendor:expr,)?
36 $(version: $version:expr,)?
37 ) => {
38 $crate::__nautilus_plugin_impl! {
39 @parse
40 name = ($($name)?),
41 vendor = ($($vendor)?),
42 version = ($($version)?),
43 }
44 };
45}
46
47#[doc(hidden)]
49#[macro_export]
50macro_rules! __nautilus_plugin_impl {
51 (
52 @parse
53 name = (),
54 $($rest:tt)*
55 ) => {
56 ::core::compile_error!("`nautilus_plugin!` requires a `name` field");
57 };
58 (
59 @parse
60 name = ($name:expr),
61 vendor = ($($vendor:expr)?),
62 version = (),
63 ) => {
64 ::core::compile_error!("`nautilus_plugin!` requires a `version` field");
65 };
66 (
67 @parse
68 name = ($name:expr),
69 vendor = ($($vendor:expr)?),
70 version = ($version:expr),
71 ) => {
72 const _: () = {
73 static MANIFEST: ::std::sync::LazyLock<$crate::manifest::PluginManifest> =
74 ::std::sync::LazyLock::new(|| $crate::manifest::PluginManifest {
75 abi_version: $crate::NAUTILUS_PLUGIN_ABI_VERSION,
76 plugin_name: $crate::boundary::BorrowedStr::from_str($name),
77 plugin_vendor: $crate::boundary::BorrowedStr::from_str(
78 $crate::__nautilus_plugin_impl!(@opt $($vendor)?),
79 ),
80 plugin_version: $crate::boundary::BorrowedStr::from_str($version),
81 build_id: $crate::manifest::PluginBuildId::current(),
82 });
83
84 #[unsafe(no_mangle)]
85 pub unsafe extern "C" fn nautilus_plugin_init(
86 host: *const $crate::host::HostVTable,
87 ) -> *const $crate::manifest::PluginManifest {
88 let result = ::std::panic::catch_unwind(|| {
89 if host.is_null() {
90 return ::core::ptr::null::<$crate::manifest::PluginManifest>();
91 }
92 &raw const *MANIFEST
93 });
94
95 match result {
96 Ok(ptr) => ptr,
97 Err(payload) => {
98 $crate::panic::drop_payload(payload);
99 ::core::ptr::null()
100 }
101 }
102 }
103 };
104 };
105
106 (@opt) => { "" };
107 (@opt $vendor:expr) => { $vendor };
108}
109
110#[cfg(test)]
111#[allow(unreachable_pub)]
112mod tests {
113 use core::{ptr, ptr::NonNull};
114
115 use rstest::rstest;
116
117 use crate::{NAUTILUS_PLUGIN_ABI_VERSION, host::HostVTable, manifest::PluginManifest};
118
119 crate::nautilus_plugin! {
120 name: "macro-test-plugin",
121 vendor: "Nautech",
122 version: "1.2.3",
123 }
124
125 unsafe extern "C" {
126 fn nautilus_plugin_init(host: *const HostVTable) -> *const PluginManifest;
127 }
128
129 #[rstest]
130 fn optional_vendor_defaults_to_empty() {
131 assert_eq!(crate::__nautilus_plugin_impl!(@opt), "");
132 }
133
134 #[rstest]
135 fn plugin_init_returns_null_for_null_host() {
136 let manifest = unsafe { nautilus_plugin_init(ptr::null()) };
138
139 assert!(manifest.is_null());
140 }
141
142 #[rstest]
143 fn plugin_init_returns_manifest_for_non_null_host() {
144 let host = NonNull::<HostVTable>::dangling().as_ptr();
145
146 let manifest = unsafe { nautilus_plugin_init(host) };
148
149 assert!(!manifest.is_null());
150 let manifest = unsafe { &*manifest };
152 assert_eq!(manifest.abi_version, NAUTILUS_PLUGIN_ABI_VERSION);
153 assert_eq!(
155 unsafe { manifest.plugin_name.as_str() },
156 "macro-test-plugin"
157 );
158 assert_eq!(unsafe { manifest.plugin_vendor.as_str() }, "Nautech");
160 assert_eq!(unsafe { manifest.plugin_version.as_str() }, "1.2.3");
162 manifest.validate().unwrap();
163 }
164}