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.13")]
106#![no_std]
107#![allow(
108 clippy::doc_markdown,
109 clippy::empty_enum,
110 clippy::expl_impl_clone_on_copy,
111 clippy::let_underscore_untyped,
112 clippy::let_unit_value,
113 clippy::must_use_candidate,
114 clippy::semicolon_if_nothing_returned, // https://github.com/rust-lang/rust-clippy/issues/7324
115)]
116
117// Not public API.
118#[doc(hidden)]
119pub extern crate core;
120
121use core::cell::UnsafeCell;
122use core::marker::PhantomData;
123use core::ops::Deref;
124use core::ptr;
125use core::sync::atomic::{AtomicPtr, Ordering};
126
127// Not public API. Used by generated code.
128#[doc(hidden)]
129pub struct Registry {
130 head: AtomicPtr<Node>,
131}
132
133// Not public API. Used by generated code.
134#[doc(hidden)]
135pub struct Node {
136 pub value: &'static dyn ErasedNode,
137 pub next: UnsafeCell<Option<&'static Node>>,
138}
139
140// The `value` is Sync, and `next` is only mutated during submit, which is prior
141// to any reads.
142unsafe impl Sync for Node {}
143
144// Not public API. Used by generated code.
145#[doc(hidden)]
146pub trait ErasedNode: Sync {
147 // SAFETY: requires *node.value is of type Self.
148 unsafe fn submit(&self, node: &'static Node);
149}
150
151impl<T: Collect> ErasedNode for T {
152 unsafe fn submit(&self, node: &'static Node) {
153 T::registry().submit(node);
154 }
155}
156
157/// Trait bound corresponding to types that can be iterated by inventory::iter.
158///
159/// This trait cannot be implemented manually. Instead use the [`collect`] macro
160/// which expands to an implementation of this trait for the given type.
161///
162/// # Examples
163///
164/// ```
165/// use inventory::Collect;
166///
167/// fn count_plugins<T: Collect>() -> usize {
168/// inventory::iter::<T>.into_iter().count()
169/// }
170/// ```
171pub trait Collect: Sync + Sized + 'static {
172 #[doc(hidden)]
173 fn registry() -> &'static Registry;
174}
175
176impl Registry {
177 // Not public API. Used by generated code.
178 pub const fn new() -> Self {
179 Registry {
180 head: AtomicPtr::new(ptr::null_mut()),
181 }
182 }
183
184 // SAFETY: requires type of *new.value matches the $ty surrounding the
185 // declaration of this registry in inventory::collect macro.
186 unsafe fn submit(&'static self, new: &'static Node) {
187 let mut head = self.head.load(Ordering::Relaxed);
188 loop {
189 *new.next.get() = head.as_ref();
190 let new_ptr = new as *const Node as *mut Node;
191 match self
192 .head
193 .compare_exchange(head, new_ptr, Ordering::Release, Ordering::Relaxed)
194 {
195 Ok(_) => return,
196 Err(prev) => head = prev,
197 }
198 }
199 }
200}
201
202/// An iterator over plugins registered of a given type.
203///
204/// The value `inventory::iter::<T>` is an iterator with element type `&'static
205/// T`.
206///
207/// There is no guarantee about the order that plugins of the same type are
208/// visited by the iterator. They may be visited in any order.
209///
210/// # Examples
211///
212/// ```
213/// # struct Flag {
214/// # short: char,
215/// # name: &'static str,
216/// # }
217/// #
218/// # inventory::collect!(Flag);
219/// #
220/// # const IGNORE: &str = stringify! {
221/// use my_flags::Flag;
222/// # };
223///
224/// fn main() {
225/// for flag in inventory::iter::<Flag> {
226/// println!("-{}, --{}", flag.short, flag.name);
227/// }
228/// }
229/// ```
230///
231/// Refer to the [crate level documentation](index.html) for a complete example
232/// of instantiating a plugin registry and submitting plugins.
233#[allow(non_camel_case_types)]
234pub type iter<T> = private::iter<T>;
235
236mod void_iter {
237 enum Void {}
238
239 #[repr(C, packed)]
240 pub struct Iter<T>([*const T; 0], Void);
241
242 unsafe impl<T> Send for Iter<T> {}
243 unsafe impl<T> Sync for Iter<T> {}
244}
245
246mod value_iter {
247 #[doc(hidden)]
248 pub use crate::private::iter::iter;
249}
250
251mod private {
252 // Based on https://github.com/dtolnay/ghost
253 #[allow(non_camel_case_types)]
254 pub enum iter<T> {
255 __Phantom(crate::void_iter::Iter<T>),
256 iter,
257 }
258
259 #[doc(hidden)]
260 pub use crate::value_iter::*;
261}
262
263#[doc(hidden)]
264pub use crate::private::*;
265
266const ITER: () = {
267 fn into_iter<T: Collect>() -> Iter<T> {
268 let head = T::registry().head.load(Ordering::Acquire);
269 Iter {
270 // Head pointer is always null or valid &'static Node.
271 node: unsafe { head.as_ref() },
272 marker: PhantomData,
273 }
274 }
275
276 impl<T: Collect> IntoIterator for iter<T> {
277 type Item = &'static T;
278 type IntoIter = Iter<T>;
279
280 fn into_iter(self) -> Self::IntoIter {
281 into_iter()
282 }
283 }
284
285 #[doc(hidden)]
286 impl<T: Collect> Deref for iter<T> {
287 type Target = fn() -> Iter<T>;
288 fn deref(&self) -> &Self::Target {
289 &(into_iter as fn() -> Iter<T>)
290 }
291 }
292
293 #[derive(Clone)]
294 pub struct Iter<T: 'static> {
295 node: Option<&'static Node>,
296 marker: PhantomData<T>,
297 }
298
299 impl<T: 'static> Iterator for Iter<T> {
300 type Item = &'static T;
301
302 fn next(&mut self) -> Option<Self::Item> {
303 let node = self.node?;
304 unsafe {
305 let value_ptr = (node.value as *const dyn ErasedNode).cast::<T>();
306 self.node = *node.next.get();
307 Some(&*value_ptr)
308 }
309 }
310 }
311};
312
313/// Associate a plugin registry with the specified type.
314///
315/// This call must be in the same crate that defines the plugin type. This macro
316/// does not "run" anything so place it outside of any function body.
317///
318/// # Examples
319///
320/// Suppose we are writing a command line flags library and want to allow any
321/// source file in the application to register command line flags that are
322/// relevant to it.
323///
324/// This is the flag registration style used by [gflags] and is better suited
325/// for large scale development than maintaining a single central list of flags,
326/// as the central list would become an endless source of merge conflicts.
327///
328/// [gflags]: https://gflags.github.io/gflags/
329///
330/// ```
331/// pub struct Flag {
332/// short: char,
333/// name: &'static str,
334/// /* ... */
335/// }
336///
337/// inventory::collect!(Flag);
338/// ```
339///
340/// Refer to the [crate level documentation](index.html) for a complete example
341/// of submitting plugins and iterating a plugin registry.
342#[macro_export]
343macro_rules! collect {
344 ($ty:ty) => {
345 impl $crate::Collect for $ty {
346 #[inline]
347 fn registry() -> &'static $crate::Registry {
348 static REGISTRY: $crate::Registry = $crate::Registry::new();
349 ®ISTRY
350 }
351 }
352 };
353}
354
355/// Enter an element into the plugin registry corresponding to its type.
356///
357/// This call may be in the same crate that defines the type, or downstream in
358/// any crate that depends on that crate.
359///
360/// This macro does not "run" anything so place it outside of any function body.
361/// In particular, note that all `submit!` invocations across all source files
362/// linked into your application all take effect simultaneously. A `submit!`
363/// invocation is not a statement that needs to be called from `main` in order
364/// to execute.
365///
366/// # Examples
367///
368/// Put `submit!` invocations outside of any function body.
369///
370/// ```
371/// # struct Flag;
372/// #
373/// # impl Flag {
374/// # const fn new(short: char, name: &'static str) -> Self {
375/// # Flag
376/// # }
377/// # }
378/// #
379/// # inventory::collect!(Flag);
380/// #
381/// inventory::submit! {
382/// Flag::new('v', "verbose")
383/// }
384/// #
385/// # fn main() {}
386/// ```
387///
388/// Do not try to invoke `submit!` from inside of a function body as it does not
389/// do what you want.
390///
391/// ```compile_fail
392/// // Do not do this.
393/// fn submit_flags(has_verbose_flag: bool) {
394/// if has_verbose_flag {
395/// inventory::submit! {
396/// Flag::new('v', "verbose")
397/// }
398/// }
399/// }
400/// ```
401///
402/// Refer to the [crate level documentation](index.html) for a complete example
403/// of instantiating and iterating a plugin registry.
404#[macro_export]
405macro_rules! submit {
406 ($($value:tt)*) => {
407 $crate::__do_submit! {
408 { $($value)* }
409 { $($value)* }
410 }
411 };
412}
413
414// Not public API.
415#[doc(hidden)]
416#[macro_export]
417macro_rules! __do_submit {
418 (used={ $($used:tt)+ } $($value:tt)*) => {
419 #[allow(non_upper_case_globals)]
420 const _: () = {
421 static __INVENTORY: $crate::Node = $crate::Node {
422 value: &{ $($value)* },
423 next: $crate::core::cell::UnsafeCell::new($crate::core::option::Option::None),
424 };
425
426 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
427 unsafe extern "C" fn __ctor() {
428 unsafe { $crate::ErasedNode::submit(__INVENTORY.value, &__INVENTORY) }
429 }
430
431 // Linux/ELF: https://www.exploit-db.com/papers/13234
432 //
433 // macOS: https://blog.timac.org/2016/0716-constructor-and-destructor-attributes/
434 //
435 // Why .CRT$XCU on Windows? https://www.cnblogs.com/sunkang/archive/2011/05/24/2055635.html
436 // 'I'=C init, 'C'=C++ init, 'P'=Pre-terminators and 'T'=Terminators
437 $($used)+
438 #[cfg_attr(
439 any(
440 target_os = "linux",
441 target_os = "android",
442 target_os = "dragonfly",
443 target_os = "freebsd",
444 target_os = "haiku",
445 target_os = "illumos",
446 target_os = "netbsd",
447 target_os = "openbsd",
448 ),
449 link_section = ".init_array",
450 )]
451 #[cfg_attr(any(target_os = "macos", target_os = "ios"), link_section = "__DATA,__mod_init_func")]
452 #[cfg_attr(windows, link_section = ".CRT$XCU")]
453 static __CTOR: unsafe extern "C" fn() = __ctor;
454 };
455 };
456
457 ({ #![used($($used:tt)+)] $($value:tt)* } { $pound:tt $bang:tt $brackets:tt $($dup:tt)* }) => {
458 $crate::__do_submit! {
459 used={ $pound $brackets }
460 $($value)*
461 }
462 };
463
464 ({ $($value:tt)* } { $($dup:tt)* }) => {
465 $crate::__do_submit! {
466 used={ #[used] }
467 $($value)*
468 }
469 };
470}
471
472#[allow(dead_code)]
473fn unused() {
474 let () = ITER;
475}