Skip to main content

zod_rs_util/error/
mod.rs

1pub mod issue;
2pub mod result;
3
4use crate::{
5    locales::{localizer, Locale},
6    ValidationIssue,
7};
8use serde_json::Value;
9use std::fmt;
10
11#[derive(Debug, Clone, PartialEq)]
12pub enum ValidationError {
13    Required,
14    InvalidType {
15        expected: ValidationType,
16        input: ValidationType,
17    },
18    InvalidValue {
19        value: String,
20    },
21    InvalidValues {
22        values: Vec<String>,
23    },
24    TooBig {
25        origin: ValidationOrigin,
26        maximum: String,
27        inclusive: bool,
28    },
29    TooSmall {
30        origin: ValidationOrigin,
31        minimum: String,
32        inclusive: bool,
33    },
34    InvalidFormat {
35        format: StringFormat,
36        detail: Option<String>,
37    },
38    InvalidNumber {
39        constraint: NumberConstraint,
40    },
41    UnrecognizedKeys {
42        keys: Vec<String>,
43    },
44    InvalidUnion {
45        issues: Vec<ValidationIssue>,
46    },
47    Custom {
48        message: String,
49    },
50}
51
52impl ValidationError {
53    pub fn local(&self, locale: Locale) -> String {
54        localizer(locale).localize(self)
55    }
56}
57
58impl fmt::Display for ValidationError {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        write!(f, "{}", self.local(Locale::En))?;
61        Ok(())
62    }
63}
64
65impl ValidationError {
66    pub fn required() -> Self {
67        Self::Required
68    }
69
70    pub fn invalid_type(expected: ValidationType, input: ValidationType) -> Self {
71        Self::InvalidType { expected, input }
72    }
73
74    pub fn invalid_value(value: impl Into<String>) -> Self {
75        Self::InvalidValue {
76            value: value.into(),
77        }
78    }
79
80    pub fn invalid_values(values: Vec<String>) -> Self {
81        Self::InvalidValues { values }
82    }
83
84    pub fn too_big(origin: ValidationOrigin, maximum: impl Into<String>, inclusive: bool) -> Self {
85        Self::TooBig {
86            origin,
87            maximum: maximum.into(),
88            inclusive,
89        }
90    }
91
92    pub fn too_small(
93        origin: ValidationOrigin,
94        minimum: impl Into<String>,
95        inclusive: bool,
96    ) -> Self {
97        Self::TooSmall {
98            origin,
99            minimum: minimum.into(),
100            inclusive,
101        }
102    }
103
104    pub fn invalid_format(format: StringFormat, detail: Option<String>) -> Self {
105        Self::InvalidFormat { format, detail }
106    }
107
108    pub fn invalid_number(constraint: NumberConstraint) -> Self {
109        Self::InvalidNumber { constraint }
110    }
111
112    pub fn unrecognized_keys(keys: Vec<String>) -> Self {
113        Self::UnrecognizedKeys { keys }
114    }
115
116    pub fn invalid_union(issues: Vec<ValidationIssue>) -> Self {
117        Self::InvalidUnion { issues }
118    }
119
120    pub fn custom(message: impl Into<String>) -> Self {
121        Self::Custom {
122            message: message.into(),
123        }
124    }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Hash)]
128pub enum ValidationOrigin {
129    String,
130    Array,
131    Number,
132}
133
134impl fmt::Display for ValidationOrigin {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        let value = match self {
137            ValidationOrigin::String => "string",
138            ValidationOrigin::Array => "array",
139            ValidationOrigin::Number => "number",
140        };
141
142        write!(f, "{value}")?;
143        Ok(())
144    }
145}
146
147#[derive(Debug, Clone, PartialEq, Eq, Hash)]
148pub enum ValidationType {
149    Null,
150    Bool,
151    Number,
152    String,
153    Array,
154    Object,
155    Undefined,
156    Custom(String),
157}
158
159impl ValidationType {
160    pub fn custom(val_type: impl Into<String>) -> Self {
161        Self::Custom(val_type.into())
162    }
163}
164
165impl From<&Value> for ValidationType {
166    fn from(value: &Value) -> Self {
167        match value {
168            Value::Null => ValidationType::Null,
169            Value::Bool(_) => ValidationType::Bool,
170            Value::Number(_) => ValidationType::Number,
171            Value::String(_) => ValidationType::String,
172            Value::Array(_) => ValidationType::Array,
173            Value::Object(_) => ValidationType::Object,
174        }
175    }
176}
177
178impl fmt::Display for ValidationType {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        let value = match self {
181            ValidationType::Null => "null",
182            ValidationType::Bool => "bool",
183            ValidationType::Number => "number",
184            ValidationType::String => "string",
185            ValidationType::Array => "array",
186            ValidationType::Object => "object",
187            ValidationType::Undefined => "undefined",
188            ValidationType::Custom(_type) => _type,
189        };
190
191        write!(f, "{value}")?;
192        Ok(())
193    }
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, Hash)]
197pub enum StringFormat {
198    StartsWith,
199    EndsWith,
200    Includes,
201    Regex,
202    Custom(String),
203}
204
205impl StringFormat {
206    pub fn custom(val_type: impl Into<String>) -> Self {
207        Self::Custom(val_type.into())
208    }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Hash)]
212pub enum NumberConstraint {
213    Finite,
214    Positive,
215    Negative,
216    NonNegative,
217    NonPositive,
218}
219
220/// Error type for parsing operations that can fail due to JSON parsing or validation.
221/// This provides better error type information than `Box<dyn Error>`.
222#[derive(Debug)]
223pub enum ParseError {
224    /// JSON parsing failed
225    Json(serde_json::Error),
226    /// Validation failed
227    Validation(result::ValidationResult),
228}
229
230impl fmt::Display for ParseError {
231    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232        match self {
233            ParseError::Json(e) => write!(f, "JSON parsing error: {}", e),
234            ParseError::Validation(e) => write!(f, "Validation error: {}", e),
235        }
236    }
237}
238
239impl std::error::Error for ParseError {
240    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
241        match self {
242            ParseError::Json(e) => Some(e),
243            ParseError::Validation(e) => Some(e),
244        }
245    }
246}
247
248impl From<serde_json::Error> for ParseError {
249    fn from(e: serde_json::Error) -> Self {
250        ParseError::Json(e)
251    }
252}
253
254impl From<result::ValidationResult> for ParseError {
255    fn from(e: result::ValidationResult) -> Self {
256        ParseError::Validation(e)
257    }
258}