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}