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}