#[cfg(feature = "inventory")]
use std::sync::OnceLock;
#[cfg(feature = "inventory")]
use inventory::iter;
use super::super::ValidatorRegistration;
use crate::NamedValidationArgument;
use crate::ValidatorRegistryError;
use crate::binding::BindError;
use crate::binding::BindErrorKind;
use crate::binding::BoundValidator;
use crate::binding::InputType;
#[derive(Debug)]
pub struct ValidatorRegistry {
registrations: Box<[ValidatorRegistration]>,
}
impl ValidatorRegistry {
pub fn from_registrations<I>(registrations: I) -> Result<Self, ValidatorRegistryError>
where
I: IntoIterator,
I::Item: Into<ValidatorRegistration>,
{
Self::build(registrations.into_iter().map(Into::into).collect())
}
#[must_use]
#[inline]
pub fn empty() -> Self {
Self {
registrations: Box::new([]),
}
}
#[cfg(feature = "inventory")]
pub fn try_global() -> Result<&'static Self, ValidatorRegistryError> {
static REGISTRY: OnceLock<Result<ValidatorRegistry, ValidatorRegistryError>> = OnceLock::new();
match REGISTRY.get_or_init(|| {
let registrations = iter::<crate::ValidatorRegistrationFactory>
.into_iter()
.map(|factory| (factory.0)())
.collect();
Self::build(registrations)
}) {
Ok(registry) => Ok(registry),
Err(error) => Err(error.clone()),
}
}
#[cfg(feature = "inventory")]
#[must_use]
pub fn global() -> &'static Self {
Self::try_global().unwrap_or_else(|error| panic!("invalid global validator registry: {error}"))
}
#[inline]
pub fn get(&self, id: &str) -> Option<&ValidatorRegistration> {
self.registrations
.binary_search_by(|registration| registration.id().as_str().cmp(id))
.ok()
.and_then(|index| self.registrations.get(index))
}
#[must_use]
#[inline]
pub fn registrations(&self) -> &[ValidatorRegistration] {
&self.registrations
}
pub fn bind(
&self,
id: &str,
input: InputType,
params: &[NamedValidationArgument<'_>],
) -> Result<BoundValidator, BindError> {
let registration = self.get(id).ok_or_else(|| BindError::new(BindErrorKind::MissingRule))?;
registration
.descriptor()
.bind_for_validated(registration.id(), input, params)
.map_err(|error| error.with_rule(registration.id()))
}
fn build(mut registrations: Vec<ValidatorRegistration>) -> Result<Self, ValidatorRegistryError> {
registrations.sort_by_key(|registration| (registration.id(), registration.source()));
let mut index = 0;
while index < registrations.len() {
let id = registrations[index].id();
let end = index
+ registrations[index..]
.iter()
.take_while(|registration| registration.id() == id)
.count();
if end - index > 1 {
let sources = registrations[index..end]
.iter()
.map(ValidatorRegistration::source)
.collect();
return Err(ValidatorRegistryError::DuplicateId {
id: id.as_str(),
sources,
});
}
index = end;
}
for registration in ®istrations {
if let Err(error) = registration.descriptor().validate_definition() {
return Err(ValidatorRegistryError::InvalidDescriptor {
id: registration.id(),
registration_source: registration.source(),
kind: error.kind(),
dependency: error.dependency().map(str::to_owned),
});
}
}
Ok(Self {
registrations: registrations.into_boxed_slice(),
})
}
}