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}