Skip to main content

qubit_validator/registry/
validator_registration_factory.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8
9//! Inventory registration factories.
10
11use crate::ValidatorRegistration;
12
13/// Factory submitted by [`register_validator!`](crate::register_validator).
14///
15/// This inventory integration type is available only with the `inventory`
16/// feature and is hidden from the normal public API documentation.
17#[doc(hidden)]
18pub struct ValidatorRegistrationFactory(
19    /// Function invoked while constructing the process-wide registry.
20    pub fn() -> ValidatorRegistration,
21);
22
23inventory::collect!(ValidatorRegistrationFactory);
24
25/// Registers a static typed validator descriptor in the optional global
26/// registry.
27///
28/// This macro is available only with the `inventory` feature. It submits a
29/// compile-time factory; the registration is materialized when
30/// [`ValidatorRegistry::try_global`](crate::ValidatorRegistry::try_global) is
31/// first called.
32/// The literal ID is validated during compilation. Invalid literal IDs are
33/// rejected before a global registry can be initialized.
34///
35/// When a registration value is already shared by local and process-wide
36/// registries, pass its path to `registration =`. This form preserves the
37/// registration's existing source metadata. The `id`/`descriptor` form
38/// instead records the macro invocation's crate, module, file, and line.
39///
40/// # Panics
41///
42/// The descriptor expression is evaluated when the registration is
43/// materialized; panics from that expression are not caught by the registry.
44///
45/// # Examples
46///
47/// ```
48/// use std::sync::Arc;
49/// use qubit_validator::ArgumentReader;
50/// use qubit_validator::BindError;
51/// use qubit_validator::InputType;
52/// use qubit_validator::PreparedOutcome;
53/// use qubit_validator::PreparedValidator;
54/// use qubit_validator::ValidatorDescriptor;
55/// use qubit_validator::ValidatorSignature;
56/// use qubit_validator::prepare_text_with_context;
57/// use qubit_validator::register_validator;
58///
59/// fn prepare(_: &mut ArgumentReader<'_>) -> Result<Arc<dyn PreparedValidator>, BindError> {
60///     Ok(prepare_text_with_context(&[], |_, _| Ok(PreparedOutcome::Valid)))
61/// }
62/// static SIGNATURES: &[ValidatorSignature] =
63///     &[ValidatorSignature::new(InputType::Text, &[], prepare)];
64/// static DESCRIPTOR: ValidatorDescriptor = ValidatorDescriptor::new(SIGNATURES);
65/// register_validator!(id = "app.valid_rule", descriptor = &DESCRIPTOR);
66/// ```
67///
68/// A shared registration can be used in both a local registry and the global
69/// inventory. The registration path preserves the source stored in the value.
70///
71/// ```
72/// use std::sync::Arc;
73/// use qubit_validator::{
74///     ArgumentReader, BindError, InputType, PreparedOutcome, PreparedValidator,
75///     RegistrationSource, ValidatorDescriptor, ValidatorId, ValidatorRegistration,
76///     ValidatorRegistry, ValidatorSignature, prepare_text_with_context,
77///     register_validator,
78/// };
79///
80/// fn prepare(_: &mut ArgumentReader<'_>) -> Result<Arc<dyn PreparedValidator>, BindError> {
81///     Ok(prepare_text_with_context(&[], |_, _| Ok(PreparedOutcome::Valid)))
82/// }
83/// static SIGNATURES: &[ValidatorSignature] =
84///     &[ValidatorSignature::new(InputType::Text, &[], prepare)];
85/// static DESCRIPTOR: ValidatorDescriptor = ValidatorDescriptor::new(SIGNATURES);
86/// const SHARED_REGISTRATION: ValidatorRegistration = ValidatorRegistration::new(
87///     ValidatorId::new("app.shared_rule"),
88///     &DESCRIPTOR,
89///     RegistrationSource::new("app", "app::rules", "src/rules.rs", 42),
90/// );
91///
92/// let local = ValidatorRegistry::from_registrations([SHARED_REGISTRATION]).unwrap();
93/// register_validator!(registration = SHARED_REGISTRATION);
94/// let global = ValidatorRegistry::try_global().unwrap();
95/// let local_entry = local.get("app.shared_rule").unwrap();
96/// let global_entry = global.get("app.shared_rule").unwrap();
97/// assert_eq!(local_entry.id(), global_entry.id());
98/// assert!(std::ptr::eq(local_entry.descriptor(), global_entry.descriptor()));
99/// assert_eq!(local_entry.source(), global_entry.source());
100/// ```
101///
102/// ```compile_fail
103/// use std::sync::Arc;
104/// use qubit_validator::ArgumentReader;
105/// use qubit_validator::BindError;
106/// use qubit_validator::InputType;
107/// use qubit_validator::PreparedOutcome;
108/// use qubit_validator::PreparedValidator;
109/// use qubit_validator::ValidatorDescriptor;
110/// use qubit_validator::ValidatorSignature;
111/// use qubit_validator::prepare_text_with_context;
112/// use qubit_validator::register_validator;
113///
114/// fn prepare(_: &mut ArgumentReader<'_>) -> Result<Arc<dyn PreparedValidator>, BindError> {
115///     Ok(prepare_text_with_context(&[], |_, _| Ok(PreparedOutcome::Valid)))
116/// }
117/// static SIGNATURES: &[ValidatorSignature] =
118///     &[ValidatorSignature::new(InputType::Text, &[], prepare)];
119/// static DESCRIPTOR: ValidatorDescriptor = ValidatorDescriptor::new(SIGNATURES);
120/// register_validator!(id = "bad..id", descriptor = &DESCRIPTOR);
121/// ```
122#[macro_export]
123macro_rules! register_validator {
124    (registration = $registration:path $(,)?) => {
125        const _: () = {
126            fn registration() -> $crate::ValidatorRegistration {
127                $registration
128            }
129
130            $crate::__private::inventory::submit! {
131                $crate::ValidatorRegistrationFactory(registration)
132            }
133        };
134    };
135    (id = $id:literal, descriptor = $descriptor:expr $(,)?) => {
136        const _: () = {
137            fn registration() -> $crate::ValidatorRegistration {
138                $crate::ValidatorRegistration::new(
139                    const { $crate::ValidatorId::new($id) },
140                    $descriptor,
141                    $crate::RegistrationSource::new(env!("CARGO_PKG_NAME"), module_path!(), file!(), line!()),
142                )
143            }
144
145            $crate::__private::inventory::submit! {
146                $crate::ValidatorRegistrationFactory(registration)
147            }
148        };
149    };
150}