Skip to main content

qubit_validator/
validator_registry_error.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//! Validator registry errors.
10
11use std::fmt::Debug;
12use std::fmt::Formatter;
13use std::fmt::Result as FmtResult;
14
15use thiserror::Error;
16
17use crate::BindErrorKind;
18use crate::RegistrationSource;
19use crate::ValidatorId;
20
21/// Failure while freezing a validator registry.
22///
23/// # Examples
24///
25/// ```
26/// use qubit_validator::{BindErrorKind, RegistrationSource, ValidatorId, ValidatorRegistryError};
27///
28/// let error = ValidatorRegistryError::InvalidDescriptor {
29///     id: ValidatorId::new("text.required"),
30///     registration_source: RegistrationSource::new("rules", "rules::text", "src/text.rs", 12),
31///     kind: BindErrorKind::InvalidDeclaration,
32///     dependency: None,
33/// };
34/// assert!(error.to_string().contains("invalid descriptor"));
35/// ```
36///
37/// # Source compatibility
38///
39/// The enum-level `non_exhaustive` attribute requires external matches to
40/// include a wildcard arm for future variants. It does not prevent external
41/// callers from constructing the current variants or matching all of their
42/// fields. Construction must supply every field of the selected variant.
43/// Patterns may use `..` to ignore fields they do not need.
44///
45/// Adding a field to an existing variant can break external construction or
46/// complete field patterns and must be evaluated as a source-compatibility
47/// change separately from adding an enum variant.
48#[must_use]
49#[derive(Clone, Error)]
50#[non_exhaustive]
51pub enum ValidatorRegistryError {
52    /// Multiple registrations claim one stable ID.
53    #[error("duplicate validator ID {id} from {sources:?}")]
54    DuplicateId {
55        /// Conflicting stable ID.
56        id: &'static str,
57        /// Registration sources in deterministic order.
58        sources: Vec<RegistrationSource>,
59    },
60    /// A registration contains an invalid descriptor declaration.
61    #[error("invalid descriptor for validator ID {id:?} from {registration_source:?}: {kind}")]
62    InvalidDescriptor {
63        /// Validator identifier.
64        id: ValidatorId,
65        /// Registration source.
66        registration_source: RegistrationSource,
67        /// Declaration error kind.
68        kind: BindErrorKind,
69        /// Dependency name attached to the declaration error, if present.
70        ///
71        /// This stores only names declared in the descriptor, not runtime
72        /// dependency values. The name is available through pattern matching
73        /// but is omitted from the default debug representation.
74        dependency: Option<String>,
75    },
76}
77
78impl Debug for ValidatorRegistryError {
79    /// Formats registration metadata without exposing dependency names.
80    ///
81    /// # Parameters
82    ///
83    /// - `self` is the registry error whose registration metadata is formatted.
84    /// - `formatter` is the destination supplied by the caller of `Debug::fmt`.
85    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
86        match self {
87            Self::DuplicateId { id, sources } => formatter
88                .debug_struct("DuplicateId")
89                .field("id", id)
90                .field("sources", sources)
91                .finish(),
92            Self::InvalidDescriptor {
93                id,
94                registration_source,
95                kind,
96                dependency,
97            } => formatter
98                .debug_struct("InvalidDescriptor")
99                .field("id", id)
100                .field("registration_source", registration_source)
101                .field("kind", kind)
102                .field("has_dependency", &dependency.is_some())
103                .finish(),
104        }
105    }
106}