Skip to main content

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}