pmpx-plugin 0.0.5

Plugin contract for pmpx: the PackageManager trait and the stable C ABI that carries it across dlopen.
Documentation
//! [`export!`](macro@crate::export) -- turn a [`PackageManager`](crate::PackageManager)
//! implementation into a whole C ABI shell.

/// Generate the plugin's entire C ABI shell: the entry symbol, plus the `name` / `family` /
/// `command` / `free_*` shims.
///
/// The usage is one line; `create` must be the path of a function returning
/// `Box<dyn PackageManager>`, not a type name, because you may want to inject construction
/// parameters in it:
/// ```ignore
/// pub fn create() -> Box<dyn pmpx_plugin::PackageManager> { Box::new(MyPlugin) }
/// pmpx_plugin::export!(create);
/// ```
///
/// This shape is agreed with the wrapper project generated by [`crate-plugin-kit`]; the two sides
/// must match.
///
/// Every call builds a new instance and deliberately caches nothing -- in one process the host
/// calls `name` / `family` / `command` once each.
///
/// [`crate-plugin-kit`]: https://crates.io/crates/crate-plugin-kit
#[macro_export]
macro_rules! export {
    ($create:path) => {
        #[doc(hidden)]
        fn __pmpx_instance() -> ::std::boxed::Box<dyn $crate::PackageManager> {
            $create()
        }

        #[doc(hidden)]
        unsafe extern "C" fn __pmpx_name() -> $crate::abi::PmpxStr {
            $crate::abi::leak_str(__pmpx_instance().name())
        }

        #[doc(hidden)]
        unsafe extern "C" fn __pmpx_family() -> $crate::abi::PmpxStr {
            $crate::abi::leak_str(__pmpx_instance().family().as_str())
        }

        #[doc(hidden)]
        unsafe extern "C" fn __pmpx_command(
            project_root: $crate::abi::PmpxStr,
            matched: *const $crate::abi::PmpxStr,
            matched_len: ::std::primitive::usize,
            verb: ::std::primitive::u32,
            args: *const $crate::abi::PmpxStr,
            args_len: ::std::primitive::usize,
            out: *mut $crate::abi::PmpxCommand,
        ) -> ::std::primitive::u32 {
            // `guard` is not optional: a panic crossing the `extern "C"` boundary aborts the
            // process and the host cannot save it.
            $crate::abi::guard(move || {
                let plugin = __pmpx_instance();
                // SAFETY: the validity of the arguments and of out is guaranteed by the caller
                // (the host) under the `PmpxPluginV1::command` contract; this just hands them to
                // an implementation under that same contract.
                unsafe {
                    $crate::abi::dispatch_command(
                        &*plugin,
                        project_root,
                        matched,
                        matched_len,
                        verb,
                        args,
                        args_len,
                        out,
                    )
                }
            })
        }

        #[doc(hidden)]
        unsafe extern "C" fn __pmpx_free_str(s: $crate::abi::PmpxStr) {
            // SAFETY: the host only calls this through the pointer in the vtable, and the vtable
            // is only produced by this macro, so whatever comes back is necessarily this side's
            // leak_* result.
            unsafe { $crate::abi::free_str(s) }
        }

        #[doc(hidden)]
        unsafe extern "C" fn __pmpx_free_command(c: *mut $crate::abi::PmpxCommand) {
            // SAFETY: as above -- only a struct filled in by this side's write_command reaches
            // here.
            unsafe { $crate::abi::free_command(c) }
        }

        #[doc(hidden)]
        static __PMPX_ENTRY: $crate::abi::PmpxPluginV1 = $crate::abi::PmpxPluginV1 {
            abi_version: $crate::abi::ABI_VERSION,
            rustc_version: $crate::abi::build_rustc(),
            target: $crate::abi::build_target(),
            name: __pmpx_name,
            family: __pmpx_family,
            command: __pmpx_command,
            free_str: __pmpx_free_str,
            free_command: __pmpx_free_command,
        };

        /// The plugin's single entry symbol. Generated by `pmpx_plugin::export!`; do not write it
        /// by hand.
        /// It uses `#[unsafe(no_mangle)]` rather than `#[no_mangle]`: the latter is a hard error
        /// in edition 2024.
        #[unsafe(no_mangle)]
        pub extern "C" fn pmpx_plugin_entry_v1() -> *const $crate::abi::PmpxPluginV1 {
            &__PMPX_ENTRY
        }
    };
}