fidius_core/registry.rs
1// Copyright 2026 Colliery, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Plugin registry assembly for multi-plugin dylibs.
16//!
17//! Each `#[plugin_impl]` submits its `PluginDescriptor` pointer via `inventory::submit!`.
18//! Plugin crates call `fidius_plugin_registry!()` once in their lib.rs to emit the
19//! `fidius_get_registry` export function that the host calls via `dlsym`.
20
21use crate::descriptor::{PluginDescriptor, PluginRegistry, FIDIUS_MAGIC, REGISTRY_VERSION};
22
23/// A submitted descriptor pointer. Used with `inventory` for distributed collection.
24pub struct DescriptorEntry {
25 pub descriptor: &'static PluginDescriptor,
26}
27
28inventory::collect!(DescriptorEntry);
29
30/// Build the plugin registry from all submitted descriptors.
31///
32/// Allocates a `Vec` of descriptor pointers and leaks it to get a `'static` pointer.
33/// Called once; the result is cached in `OnceLock`.
34fn build_registry() -> PluginRegistry {
35 let entries: Vec<*const PluginDescriptor> = inventory::iter::<DescriptorEntry>()
36 .map(|e| e.descriptor as *const PluginDescriptor)
37 .collect();
38
39 let count = entries.len() as u32;
40 let ptr = entries.as_ptr();
41 std::mem::forget(entries);
42
43 PluginRegistry {
44 magic: FIDIUS_MAGIC,
45 registry_version: REGISTRY_VERSION,
46 plugin_count: count,
47 descriptors: ptr,
48 }
49}
50
51/// Get or build the plugin registry.
52///
53/// Returns a `'static` reference to the registry. Built on first call,
54/// cached for subsequent calls.
55pub fn get_registry() -> &'static PluginRegistry {
56 static REGISTRY: std::sync::OnceLock<PluginRegistry> = std::sync::OnceLock::new();
57 REGISTRY.get_or_init(build_registry)
58}
59
60/// Emit the `fidius_get_registry` export function.
61///
62/// Call this once in your plugin cdylib's `lib.rs`. The host calls
63/// `dlsym("fidius_get_registry")` and invokes it to get the registry.
64///
65/// ```ignore
66/// fidius::fidius_plugin_registry!();
67/// ```
68#[macro_export]
69macro_rules! fidius_plugin_registry {
70 () => {
71 #[no_mangle]
72 pub extern "C" fn fidius_get_registry() -> *const $crate::descriptor::PluginRegistry {
73 $crate::registry::get_registry() as *const _
74 }
75 };
76}