inventory/
lib.rs

1//! [![github]](https://github.com/dtolnay/inventory) [![crates-io]](https://crates.io/crates/inventory) [![docs-rs]](https://docs.rs/inventory)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! **Typed distributed plugin registration.**
10//!
11//! This crate provides a way to set up a plugin registry into which plugins
12//! can be registered from any source file linked into your application. There
13//! does not need to be a central list of all the plugins.
14//!
15//! # Examples
16//!
17//! Suppose we are writing a command line flags library and want to allow any
18//! source file in the application to register command line flags that are
19//! relevant to it.
20//!
21//! This is the flag registration style used by [gflags] and is better suited
22//! for large scale development than maintaining a single central list of flags,
23//! as the central list would become an endless source of merge conflicts in an
24//! application developed simultaneously by thousands of developers.
25//!
26//! [gflags]: https://gflags.github.io/gflags/
27//!
28//! ## Instantiating the plugin registry
29//!
30//! Let's use a `struct Flag` as the plugin type, which will contain the short
31//! name of the flag like `-v`, the full name like `--verbose`, and maybe other
32//! information like argument type and help text. We instantiate a plugin
33//! registry with an invocation of `inventory::collect!`.
34//!
35//! ```
36//! pub struct Flag {
37//!     short: char,
38//!     name: &'static str,
39//!     /* ... */
40//! }
41//!
42//! impl Flag {
43//!     pub const fn new(short: char, name: &'static str) -> Self {
44//!         Flag { short, name }
45//!     }
46//! }
47//!
48//! inventory::collect!(Flag);
49//! ```
50//!
51//! This `collect!` call must be in the same crate that defines the plugin type.
52//! This macro does not "run" anything so place it outside of any function body.
53//!
54//! ## Registering plugins
55//!
56//! Now any crate with access to the `Flag` type can register flags as a plugin.
57//! Plugins can be registered by the same crate that declares the plugin type,
58//! or by any downstream crate.
59//!
60//! ```
61//! # struct Flag;
62//! #
63//! # impl Flag {
64//! #     const fn new(short: char, name: &'static str) -> Self {
65//! #         Flag
66//! #     }
67//! # }
68//! #
69//! # inventory::collect!(Flag);
70//! #
71//! inventory::submit! {
72//!     Flag::new('v', "verbose")
73//! }
74//! #
75//! # fn main() {}
76//! ```
77//!
78//! The `submit!` macro does not "run" anything so place it outside of any
79//! function body. In particular, note that all `submit!` invocations across all
80//! source files linked into your application all take effect simultaneously. A
81//! `submit!` invocation is not a statement that needs to be called from `main`
82//! in order to execute.
83//!
84//! ## Iterating over plugins
85//!
86//! The value `inventory::iter::<T>` is an iterator with element type `&'static
87//! T` that iterates over all plugins registered of type `T`.
88//!
89//! ```
90//! # struct Flag {
91//! #     short: char,
92//! #     name: &'static str,
93//! # }
94//! #
95//! # inventory::collect!(Flag);
96//! #
97//! for flag in inventory::iter::<Flag> {
98//!     println!("-{}, --{}", flag.short, flag.name);
99//! }
100//! ```
101//!
102//! There is no guarantee about the order that plugins of the same type are
103//! visited by the iterator. They may be visited in any order.
104
105#![doc(html_root_url = "https://docs.rs/inventory/0.3.16")]
106#![no_std]
107#![deny(unsafe_op_in_unsafe_fn)]
108#![allow(
109    clippy::doc_markdown,
110    clippy::empty_enum,
111    clippy::expl_impl_clone_on_copy,
112    clippy::let_underscore_untyped,
113    clippy::let_unit_value,
114    clippy::must_use_candidate,
115    clippy::new_without_default,
116    clippy::semicolon_if_nothing_returned, // https://github.com/rust-lang/rust-clippy/issues/7324
117)]
118
119// Not public API.
120#[doc(hidden)]
121pub extern crate core;
122
123use core::cell::UnsafeCell;
124use core::marker::PhantomData;
125use core::ops::Deref;
126use core::ptr;
127use core::sync::atomic::{AtomicPtr, Ordering};
128
129// Not public API. Used by generated code.
130#[doc(hidden)]
131pub struct Registry {
132    head: AtomicPtr<Node>,
133}
134
135// Not public API. Used by generated code.
136#[doc(hidden)]
137pub struct Node {
138    pub value: &'static dyn ErasedNode,
139    pub next: UnsafeCell<Option<&'static Node>>,
140}
141
142// The `value` is Sync, and `next` is only mutated during submit, which is prior
143// to any reads.
144unsafe impl Sync for Node {}
145
146// Not public API. Used by generated code.
147#[doc(hidden)]
148pub trait ErasedNode: Sync {
149    // SAFETY: requires *node.value is of type Self.
150    unsafe fn submit(&self, node: &'static Node);
151}
152
153impl<T: Collect> ErasedNode for T {
154    unsafe fn submit(&self, node: &'static Node) {
155        unsafe {
156            T::registry().submit(node);
157        }
158    }
159}
160
161/// Trait bound corresponding to types that can be iterated by inventory::iter.
162///
163/// This trait cannot be implemented manually. Instead use the [`collect`] macro
164/// which expands to an implementation of this trait for the given type.
165///
166/// # Examples
167///
168/// ```
169/// use inventory::Collect;
170///
171/// fn count_plugins<T: Collect>() -> usize {
172///     inventory::iter::<T>.into_iter().count()
173/// }
174/// ```
175pub trait Collect: Sync + Sized + 'static {
176    #[doc(hidden)]
177    fn registry() -> &'static Registry;
178}
179
180impl Registry {
181    // Not public API. Used by generated code.
182    pub const fn new() -> Self {
183        Registry {
184            head: AtomicPtr::new(ptr::null_mut()),
185        }
186    }
187
188    // SAFETY: requires type of *new.value matches the $ty surrounding the
189    // declaration of this registry in inventory::collect macro.
190    unsafe fn submit(&'static self, new: &'static Node) {
191        let mut head = self.head.load(Ordering::Relaxed);
192        loop {
193            unsafe {
194                *new.next.get() = head.as_ref();
195            }
196            let new_ptr = new as *const Node as *mut Node;
197            match self
198                .head
199                .compare_exchange(head, new_ptr, Ordering::Release, Ordering::Relaxed)
200            {
201                Ok(_) => return,
202                Err(prev) => head = prev,
203            }
204        }
205    }
206}
207
208/// An iterator over plugins registered of a given type.
209///
210/// The value `inventory::iter::<T>` is an iterator with element type `&'static
211/// T`.
212///
213/// There is no guarantee about the order that plugins of the same type are
214/// visited by the iterator. They may be visited in any order.
215///
216/// # Examples
217///
218/// ```
219/// # struct Flag {
220/// #     short: char,
221/// #     name: &'static str,
222/// # }
223/// #
224/// # inventory::collect!(Flag);
225/// #
226/// # const IGNORE: &str = stringify! {
227/// use my_flags::Flag;
228/// # };
229///
230/// fn main() {
231///     for flag in inventory::iter::<Flag> {
232///         println!("-{}, --{}", flag.short, flag.name);
233///     }
234/// }
235/// ```
236///
237/// Refer to the [crate level documentation](index.html) for a complete example
238/// of instantiating a plugin registry and submitting plugins.
239#[allow(non_camel_case_types)]
240pub type iter<T> = private::iter<T>;
241
242mod void_iter {
243    enum Void {}
244
245    #[repr(C, packed)]
246    pub struct Iter<T>([*const T; 0], Void);
247
248    unsafe impl<T> Send for Iter<T> {}
249    unsafe impl<T> Sync for Iter<T> {}
250}
251
252mod value_iter {
253    #[doc(hidden)]
254    pub use crate::private::iter::iter;
255}
256
257mod private {
258    // Based on https://github.com/dtolnay/ghost
259    #[allow(non_camel_case_types)]
260    pub enum iter<T> {
261        __Phantom(crate::void_iter::Iter<T>),
262        iter,
263    }
264
265    #[doc(hidden)]
266    pub use crate::value_iter::*;
267}
268
269#[doc(hidden)]
270pub use crate::private::*;
271
272const _: () = {
273    fn into_iter<T: Collect>() -> Iter<T> {
274        let head = T::registry().head.load(Ordering::Acquire);
275        Iter {
276            // Head pointer is always null or valid &'static Node.
277            node: unsafe { head.as_ref() },
278            marker: PhantomData,
279        }
280    }
281
282    impl<T: Collect> IntoIterator for iter<T> {
283        type Item = &'static T;
284        type IntoIter = Iter<T>;
285
286        fn into_iter(self) -> Self::IntoIter {
287            into_iter()
288        }
289    }
290
291    #[doc(hidden)]
292    impl<T: Collect> Deref for iter<T> {
293        type Target = fn() -> Iter<T>;
294        fn deref(&self) -> &Self::Target {
295            &(into_iter as fn() -> Iter<T>)
296        }
297    }
298
299    #[derive(Clone)]
300    pub struct Iter<T: 'static> {
301        node: Option<&'static Node>,
302        marker: PhantomData<T>,
303    }
304
305    impl<T: 'static> Iterator for Iter<T> {
306        type Item = &'static T;
307
308        fn next(&mut self) -> Option<Self::Item> {
309            let node = self.node?;
310            unsafe {
311                let value_ptr = (node.value as *const dyn ErasedNode).cast::<T>();
312                self.node = *node.next.get();
313                Some(&*value_ptr)
314            }
315        }
316    }
317};
318
319/// Associate a plugin registry with the specified type.
320///
321/// This call must be in the same crate that defines the plugin type. This macro
322/// does not "run" anything so place it outside of any function body.
323///
324/// # Examples
325///
326/// Suppose we are writing a command line flags library and want to allow any
327/// source file in the application to register command line flags that are
328/// relevant to it.
329///
330/// This is the flag registration style used by [gflags] and is better suited
331/// for large scale development than maintaining a single central list of flags,
332/// as the central list would become an endless source of merge conflicts.
333///
334/// [gflags]: https://gflags.github.io/gflags/
335///
336/// ```
337/// pub struct Flag {
338///     short: char,
339///     name: &'static str,
340///     /* ... */
341/// }
342///
343/// inventory::collect!(Flag);
344/// ```
345///
346/// Refer to the [crate level documentation](index.html) for a complete example
347/// of submitting plugins and iterating a plugin registry.
348#[macro_export]
349macro_rules! collect {
350    ($ty:ty) => {
351        impl $crate::Collect for $ty {
352            #[inline]
353            fn registry() -> &'static $crate::Registry {
354                static REGISTRY: $crate::Registry = $crate::Registry::new();
355                &REGISTRY
356            }
357        }
358    };
359}
360
361/// Enter an element into the plugin registry corresponding to its type.
362///
363/// This call may be in the same crate that defines the type, or downstream in
364/// any crate that depends on that crate.
365///
366/// This macro does not "run" anything so place it outside of any function body.
367/// In particular, note that all `submit!` invocations across all source files
368/// linked into your application all take effect simultaneously. A `submit!`
369/// invocation is not a statement that needs to be called from `main` in order
370/// to execute.
371///
372/// # Examples
373///
374/// Put `submit!` invocations outside of any function body.
375///
376/// ```
377/// # struct Flag;
378/// #
379/// # impl Flag {
380/// #     const fn new(short: char, name: &'static str) -> Self {
381/// #         Flag
382/// #     }
383/// # }
384/// #
385/// # inventory::collect!(Flag);
386/// #
387/// inventory::submit! {
388///     Flag::new('v', "verbose")
389/// }
390/// #
391/// # fn main() {}
392/// ```
393///
394/// Do not try to invoke `submit!` from inside of a function body as it does not
395/// do what you want.
396///
397/// ```compile_fail
398/// // Do not do this.
399/// fn submit_flags(has_verbose_flag: bool) {
400///     if has_verbose_flag {
401///         inventory::submit! {
402///             Flag::new('v', "verbose")
403///         }
404///     }
405/// }
406/// ```
407///
408/// Refer to the [crate level documentation](index.html) for a complete example
409/// of instantiating and iterating a plugin registry.
410#[macro_export]
411macro_rules! submit {
412    ($($value:tt)*) => {
413        $crate::__do_submit! {
414            { $($value)* }
415            { $($value)* }
416        }
417    };
418}
419
420// Not public API.
421#[cfg(any(target_os = "emscripten", target_os = "wasi"))]
422#[doc(hidden)]
423pub mod __private {
424    #[doc(hidden)]
425    pub use rustversion::attr;
426}
427
428// Not public API.
429#[doc(hidden)]
430#[macro_export]
431macro_rules! __do_submit {
432    (used={ $($used:tt)+ } $($value:tt)*) => {
433        #[allow(non_upper_case_globals)]
434        const _: () = {
435            static __INVENTORY: $crate::Node = $crate::Node {
436                value: &{ $($value)* },
437                next: $crate::core::cell::UnsafeCell::new($crate::core::option::Option::None),
438            };
439
440            #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
441            unsafe extern "C" fn __ctor() {
442                unsafe { $crate::ErasedNode::submit(__INVENTORY.value, &__INVENTORY) }
443            }
444
445            // Linux/ELF: https://www.exploit-db.com/papers/13234
446            //
447            // macOS: https://blog.timac.org/2016/0716-constructor-and-destructor-attributes/
448            //
449            // Why .CRT$XCU on Windows? https://www.cnblogs.com/sunkang/archive/2011/05/24/2055635.html
450            // 'I'=C init, 'C'=C++ init, 'P'=Pre-terminators and 'T'=Terminators
451            $($used)+
452            #[cfg_attr(
453                any(
454                    target_os = "linux",
455                    target_os = "android",
456                    target_os = "dragonfly",
457                    target_os = "freebsd",
458                    target_os = "haiku",
459                    target_os = "illumos",
460                    target_os = "netbsd",
461                    target_os = "openbsd",
462                    target_os = "none",
463                ),
464                link_section = ".init_array",
465            )]
466            #[cfg_attr(
467                any(target_os = "emscripten", target_os = "wasi"),
468                $crate::__private::attr(
469                    any(all(stable, since(1.85)), since(2024-12-18)),
470                    link_section = ".init_array",
471                ),
472            )]
473            #[cfg_attr(
474                any(target_os = "macos", target_os = "ios"),
475                link_section = "__DATA,__mod_init_func",
476            )]
477            #[cfg_attr(windows, link_section = ".CRT$XCU")]
478            static __CTOR: unsafe extern "C" fn() = __ctor;
479        };
480    };
481
482    ({ #![used($($used:tt)+)] $($value:tt)* } { $pound:tt $bang:tt $brackets:tt $($dup:tt)* }) => {
483        $crate::__do_submit! {
484            used={ $pound $brackets }
485            $($value)*
486        }
487    };
488
489    ({ $($value:tt)* } { $($dup:tt)* }) => {
490        $crate::__do_submit! {
491            used={ #[used] }
492            $($value)*
493        }
494    };
495}