cjtoolkit_structured_validator/common/validation_collector.rs
1//! This module contains structures and traits for working with validation errors.
2
3use crate::common::locale::LocaleMessage;
4use blake3::Hash;
5use std::fmt::Debug;
6use std::sync::Arc;
7
8/// `ValidateErrorStore` is a structure used to store validation errors, where each error consists
9/// of a `String` key and an associated `Box<dyn LocaleMessage>` value. The key represents
10/// an identifier (e.g., field name or error code), while the `LocaleMessage` represents
11/// a localizable message for the associated validation error.
12///
13/// This structure is designed to be `Default` and makes use of an `Arc<[]>` to share ownership
14/// of the data, enabling efficient cloning and concurrent usage in multithreaded contexts.
15///
16/// # Fields
17/// - `0`: A reference-counted array (`Arc<[]>`) of tuples containing:
18/// - `String`: The identifier for the validation error.
19/// - `Box<dyn LocaleMessage>`: A boxed trait object to represent a localizable message dynamically.
20///
21/// # Traits
22/// The struct derives the `Default` trait so it can be initialized with an empty error store.
23///
24#[derive(Default)]
25pub struct ValidateErrorStore(pub Arc<[(String, Box<dyn LocaleMessage>)]>);
26
27impl Debug for ValidateErrorStore {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 for (i, error) in self.0.iter().enumerate() {
30 if i > 0 {
31 write!(f, ", ")?;
32 }
33 write!(f, "{:?}", error.0)?;
34 }
35 Ok(())
36 }
37}
38
39impl PartialEq for ValidateErrorStore {
40 fn eq(&self, other: &Self) -> bool {
41 self.hash() == other.hash()
42 }
43}
44
45impl Clone for ValidateErrorStore {
46 fn clone(&self) -> Self {
47 Self(Arc::clone(&self.0))
48 }
49}
50
51impl ValidateErrorStore {
52 /// Converts the internal vector representation of the original message
53 /// into an `Arc<[String]>`, a thread-safe, shared, and reference-counted slice of strings.
54 ///
55 /// # Returns
56 /// An `Arc<[String]>` containing the original message as a shared reference. The
57 /// `Arc` allows multiple threads to safely share ownership of the message data without
58 /// requiring additional cloning or copying of the underlying strings.
59 ///
60 /// # Usage
61 /// This method can be used when you need a thread-safe, immutable reference
62 /// to the original message structure, allowing for efficient sharing of data
63 /// across threads.
64 ///
65 /// # Implementation
66 /// Internally, the method calls `as_original_message_vec()` to retrieve the original
67 /// message as a `Vec<String>` and then converts it to an `Arc<[String]>`.
68 ///
69 pub fn as_original_message(&self) -> Arc<[String]> {
70 self.as_original_message_vec().into()
71 }
72
73 /// Converts the current collection into a `Vec<String>` containing the original messages.
74 ///
75 /// # Description
76 /// This method iterates over the elements of the internal collection, extracts the original
77 /// message (assumed to be stored as the first element of each component, `e.0`), clones it,
78 /// and collects all the cloned messages into a new `Vec<String>`.
79 ///
80 /// # Returns
81 /// A `Vec<String>` containing the cloned original messages from each element in the collection.
82 ///
83 /// # Notes
84 /// - The internal structure `self.0` must be iterable, and each element must have a
85 /// `0` field containing a `String`-like value.
86 /// - The result is a completely new vector and does not modify the internal state of the
87 /// current collection.
88 ///
89 /// # Complexity
90 /// This method has a time complexity of O(n), where n is the number of elements in
91 /// the internal collection `self.0`.
92 pub fn as_original_message_vec(&self) -> Vec<String> {
93 self.0.iter().map(|e| e.0.clone()).collect()
94 }
95
96 /// Converts the current instance into a `ValidateErrorCollector`.
97 ///
98 /// This method takes the current object, clones it, and converts it into a
99 /// `ValidateErrorCollector` instance. It is useful when you want to transform
100 /// the current object to a `ValidateErrorCollector` for further processing or
101 /// validation error handling.
102 ///
103 /// # Returns
104 ///
105 /// A `ValidateErrorCollector` created by cloning and converting the current object.
106 pub fn as_validate_error_collector(&self) -> ValidateErrorCollector {
107 self.clone().into()
108 }
109
110 fn hash(&self) -> Hash {
111 let mut hasher = blake3::Hasher::new();
112 for error in self.0.iter() {
113 hasher.update(error.0.as_bytes());
114 }
115 hasher.finalize()
116 }
117}
118
119impl Into<ValidateErrorCollector> for ValidateErrorStore {
120 fn into(self) -> ValidateErrorCollector {
121 let mut errors: Vec<(String, Box<dyn LocaleMessage>)> = vec![];
122 for error in self.0.iter() {
123 errors.push((error.0.clone(), Box::new(error.1.get_locale_data())));
124 }
125 ValidateErrorCollector(errors)
126 }
127}
128
129/// A struct for collecting validation errors in a list.
130///
131/// `ValidateErrorCollector` is used to gather validation errors that can be
132/// associated with a specific field or key. Each error is stored as a tuple containing:
133/// - A `String` representing the field or key name where the error occurred.
134/// - A `Box<dyn LocaleMessage>` representing a localized error message.
135///
136/// # Fields
137/// - `0`: A vector of tuples, each tuple containing a field name as `String` and a
138/// localized error message as `Box<dyn LocaleMessage>`.
139///
140/// Note: The `LocaleMessage` trait is used to encapsulate errors with localization support.
141/// Implementations of `LocaleMessage` should provide mechanisms for translating error messages
142/// to various locales.
143#[derive(Default)]
144pub struct ValidateErrorCollector(pub Vec<(String, Box<dyn LocaleMessage>)>);
145
146impl Into<ValidateErrorStore> for ValidateErrorCollector {
147 fn into(self) -> ValidateErrorStore {
148 ValidateErrorStore(self.0.into())
149 }
150}
151
152impl ValidateErrorCollector {
153 /// Creates a new instance of the struct with an empty `Vec`.
154 ///
155 /// # Returns
156 /// A new instance of the struct containing an empty `Vec`.
157 ///
158 /// # Examples
159 ///
160 /// ```
161 /// use cjtoolkit_structured_validator::common::locale::ValidateErrorCollector;
162 /// let instance = ValidateErrorCollector::new();
163 /// assert!(instance.0.is_empty());
164 /// ```
165 pub fn new() -> Self {
166 Self(vec![])
167 }
168
169 /// Checks whether the container is empty.
170 ///
171 /// This method returns `true` if the container has no elements, and `false` otherwise.
172 ///
173 /// # Returns
174 /// * `true` - If the container is empty.
175 /// * `false` - If the container contains one or more elements.
176 ///
177 /// # Examples
178 ///
179 /// ```
180 /// use cjtoolkit_structured_validator::common::locale::ValidateErrorCollector;
181 /// let container = ValidateErrorCollector::new();
182 /// assert!(container.is_empty());
183 /// ```
184 pub fn is_empty(&self) -> bool {
185 self.0.is_empty()
186 }
187
188 ///
189 /// Adds an error item to the collection.
190 ///
191 /// # Parameters
192 /// - `error`: A tuple containing:
193 /// - A `String` representing the error message or identifier.
194 /// - A `Box<dyn LocaleMessage>` which encapsulates a trait object implementing `LocaleMessage`.
195 /// This provides localized details for the error.
196 ///
197 /// # Behavior
198 /// Appends the given `error` tuple to the internal vector storing errors.
199 ///
200 pub fn push(&mut self, error: (String, Box<dyn LocaleMessage>)) {
201 self.0.push(error);
202 }
203
204 /// Returns the number of elements in the collection.
205 ///
206 /// This method provides the length of the underlying collection by
207 /// delegating the call to the `.len()` method of the inner data structure.
208 ///
209 /// # Returns
210 /// * `usize` - The number of elements currently contained in the collection.
211 ///
212 pub fn len(&self) -> usize {
213 self.0.len()
214 }
215}
216
217/// A trait that provides an abstraction to interact with and retrieve validation-related data
218/// such as error stores, error collectors, and original messages.
219///
220/// Implementors of this trait are expected to provide a mechanism to convert or extract
221/// their underlying structure into a `ValidateErrorStore`, which can then be used to
222/// access detailed validation-related data.
223///
224/// This trait also provides default implementations for retrieving validation error
225/// collectors and original messages, relying on the `ValidateErrorStore` for such operations.
226pub trait AsValidateErrorStore {
227 fn as_validate_store(&self) -> ValidateErrorStore;
228
229 fn as_validate_error_collector(&self) -> ValidateErrorCollector {
230 self.as_validate_store().as_validate_error_collector()
231 }
232
233 fn as_original_message_vec(&self) -> Vec<String> {
234 self.as_validate_store().as_original_message_vec()
235 }
236
237 fn as_original_message(&self) -> Arc<[String]> {
238 self.as_validate_store().as_original_message()
239 }
240}
241
242impl<T, E> AsValidateErrorStore for Result<T, E>
243where
244 for<'a> &'a E: Into<ValidateErrorStore>,
245{
246 fn as_validate_store(&self) -> ValidateErrorStore {
247 self.as_ref().err().map(Into::into).unwrap_or_default()
248 }
249}