Skip to main content

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 {}