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    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
81        match self {
82            Self::DuplicateId { id, sources } => formatter
83                .debug_struct("DuplicateId")
84                .field("id", id)
85                .field("sources", sources)
86                .finish(),
87            Self::InvalidDescriptor {
88                id,
89                registration_source,
90                kind,
91                dependency,
92            } => formatter
93                .debug_struct("InvalidDescriptor")
94                .field("id", id)
95                .field("registration_source", registration_source)
96                .field("kind", kind)
97                .field("has_dependency", &dependency.is_some())
98                .finish(),
99        }
100    }
101}