qubit_validator/binding/bind_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//! Structured validation binding failures.
10
11use std::error::Error;
12use std::fmt;
13
14use super::BindErrorKind;
15use crate::ValidatorId;
16
17/// A binding error that does not retain raw parameter values.
18///
19/// # Examples
20///
21/// ```
22/// use qubit_validator::{BindError, BindErrorKind};
23///
24/// let error = BindError::new(BindErrorKind::MissingParameter).with_parameter("minimum");
25/// assert_eq!(error.parameter(), Some("minimum"));
26/// assert!(!error.to_string().contains("secret value"));
27/// ```
28#[must_use]
29pub struct BindError {
30 /// Stable category describing the failed binding operation.
31 kind: BindErrorKind,
32 /// Optional rule identifier associated with the failure.
33 rule_id: Option<ValidatorId>,
34 /// Optional parameter name; the parameter value is never retained.
35 parameter: Option<String>,
36 /// Optional dependency name; the dependency value is never retained.
37 dependency: Option<String>,
38}
39
40impl BindError {
41 /// Creates a binding error for the supplied failure category.
42 ///
43 /// # Parameters
44 ///
45 /// - `kind`: Stable category describing why binding failed.
46 ///
47 /// # Returns
48 ///
49 /// A new error without an associated rule, parameter, or dependency.
50 #[inline]
51 pub fn new(kind: BindErrorKind) -> Self {
52 Self {
53 kind,
54 rule_id: None,
55 parameter: None,
56 dependency: None,
57 }
58 }
59
60 /// Returns the error kind.
61 ///
62 /// # Returns
63 ///
64 /// The stable category describing the binding failure.
65 #[inline]
66 pub const fn kind(&self) -> BindErrorKind {
67 self.kind
68 }
69
70 /// Returns the resolved rule identifier attached to this error, if any.
71 ///
72 /// A registry lookup that fails with `MissingRule` has no resolved
73 /// [`ValidatorId`], so this returns `None`. The caller retains the
74 /// requested ID text when it needs to report which lookup failed.
75 ///
76 /// # Returns
77 ///
78 /// The resolved identifier for a selected rule, or `None` when no rule
79 /// was selected.
80 #[inline]
81 pub const fn rule_id(&self) -> Option<ValidatorId> {
82 self.rule_id
83 }
84
85 /// Returns the associated parameter name, if any.
86 ///
87 /// The error never retains the corresponding raw parameter value.
88 ///
89 /// # Returns
90 ///
91 /// Returns `Some` when a parameter has been attached, or `None` otherwise.
92 #[inline]
93 pub fn parameter(&self) -> Option<&str> {
94 self.parameter.as_deref()
95 }
96
97 /// Returns the associated dependency name, if any.
98 ///
99 /// The error never retains the corresponding raw dependency value.
100 ///
101 /// # Returns
102 ///
103 /// Returns `Some` when a dependency has been attached, or `None` otherwise.
104 #[inline]
105 pub fn dependency(&self) -> Option<&str> {
106 self.dependency.as_deref()
107 }
108
109 /// Associates the resolved rule identifier with this error.
110 ///
111 /// # Parameters
112 ///
113 /// - `rule_id`: Identifier of the rule whose binding failed.
114 ///
115 /// # Returns
116 ///
117 /// This error with the identifier attached.
118 #[inline]
119 pub const fn with_rule(mut self, rule_id: ValidatorId) -> Self {
120 self.rule_id = Some(rule_id);
121 self
122 }
123
124 /// Associates a parameter name with this error without retaining its value.
125 ///
126 /// # Parameters
127 ///
128 /// - `parameter`: Name of the parameter involved in the failure.
129 ///
130 /// # Returns
131 ///
132 /// This error with the parameter name attached.
133 pub fn with_parameter(mut self, parameter: impl Into<String>) -> Self {
134 self.parameter = Some(parameter.into());
135 self
136 }
137
138 /// Associates a dependency name with this error without retaining its
139 /// value.
140 ///
141 /// # Parameters
142 ///
143 /// - `dependency`: Name of the dependency involved in the failure.
144 ///
145 /// # Returns
146 ///
147 /// This error with the dependency name attached.
148 pub fn with_dependency(mut self, dependency: impl Into<String>) -> Self {
149 self.dependency = Some(dependency.into());
150 self
151 }
152}
153
154impl fmt::Debug for BindError {
155 /// Formats structural metadata without exposing raw parameter values.
156 ///
157 /// # Parameters
158 ///
159 /// - `formatter`: Formatter that receives the structural representation.
160 ///
161 /// # Errors
162 ///
163 /// Returns [`fmt::Error`] if writing the debug representation fails.
164 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
165 formatter
166 .debug_struct("BindError")
167 .field("kind", &self.kind)
168 .field("rule_id", &self.rule_id)
169 .field("has_parameter", &self.parameter.is_some())
170 .field("has_dependency", &self.dependency.is_some())
171 .finish()
172 }
173}
174
175impl fmt::Display for BindError {
176 /// Formats a stable summary without exposing raw parameter values.
177 ///
178 /// # Parameters
179 ///
180 /// - `formatter`: Formatter that receives the stable error summary.
181 ///
182 /// # Errors
183 ///
184 /// Returns [`fmt::Error`] if writing the summary fails.
185 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(formatter, "validation binding failed: {}", self.kind)
187 }
188}
189
190impl Error for BindError {}