1use axum::response::{IntoResponse, Response};
4use http::StatusCode;
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, thiserror::Error)]
9pub enum FormError {
10 #[error("Form not found: {0}")]
11 NotFound(String),
12
13 #[error("Step not found: {0}")]
14 StepNotFound(String),
15
16 #[error("Field not found: {0}")]
17 FieldNotFound(String),
18
19 #[error("Validation failed")]
20 ValidationFailed(ValidationErrors),
21
22 #[error("Validation failed")]
23 StepValidationFailed(StepValidationErrors),
24
25 #[error("Database error: {0}")]
26 Database(String),
27
28 #[error("Invalid field type: {0}")]
29 InvalidFieldType(String),
30
31 #[error("Condition evaluation failed: {0}")]
32 ConditionError(String),
33
34 #[error("File upload error: {0}")]
35 FileUpload(String),
36
37 #[error("Invalid form data: {0}")]
38 InvalidData(String),
39
40 #[error("Form is deleted")]
41 FormDeleted,
42
43 #[error("Submission not found: {0}")]
44 SubmissionNotFound(String),
45}
46
47impl FormError {
48 #[must_use]
50 pub fn status_code(&self) -> StatusCode {
51 match self {
52 Self::NotFound(_)
53 | Self::StepNotFound(_)
54 | Self::FieldNotFound(_)
55 | Self::SubmissionNotFound(_) => StatusCode::NOT_FOUND,
56 Self::ValidationFailed(_)
57 | Self::StepValidationFailed(_)
58 | Self::InvalidFieldType(_)
59 | Self::InvalidData(_) => StatusCode::BAD_REQUEST,
60 Self::FormDeleted => StatusCode::GONE,
61 Self::Database(_) | Self::ConditionError(_) => StatusCode::INTERNAL_SERVER_ERROR,
62 Self::FileUpload(_) => StatusCode::BAD_REQUEST,
63 }
64 }
65
66 #[must_use]
68 pub fn error_code(&self) -> &'static str {
69 match self {
70 Self::NotFound(_) => "FORM_NOT_FOUND",
71 Self::StepNotFound(_) => "STEP_NOT_FOUND",
72 Self::FieldNotFound(_) => "FIELD_NOT_FOUND",
73 Self::ValidationFailed(_) | Self::StepValidationFailed(_) => "VALIDATION_FAILED",
74 Self::Database(_) => "DATABASE_ERROR",
75 Self::InvalidFieldType(_) => "INVALID_FIELD_TYPE",
76 Self::ConditionError(_) => "CONDITION_ERROR",
77 Self::FileUpload(_) => "FILE_UPLOAD_ERROR",
78 Self::InvalidData(_) => "INVALID_DATA",
79 Self::FormDeleted => "FORM_DELETED",
80 Self::SubmissionNotFound(_) => "SUBMISSION_NOT_FOUND",
81 }
82 }
83}
84
85impl IntoResponse for FormError {
86 fn into_response(self) -> Response {
87 let api_response: crate::response::ApiResponse<()> = self.into();
88 api_response.into_response()
89 }
90}
91
92impl From<FormError> for crate::response::ApiResponse<()> {
93 fn from(err: FormError) -> Self {
94 match &err {
95 FormError::ValidationFailed(errors) => {
96 crate::response::ApiResponse::validation_failed(errors.clone())
97 }
98 FormError::StepValidationFailed(errors) => {
99 crate::response::ApiResponse::step_validation_failed(errors.clone())
100 }
101 _ => crate::response::ApiResponse::error(
102 err.error_code(),
103 err.to_string(),
104 err.status_code(),
105 ),
106 }
107 }
108}
109
110impl From<sea_orm::DbErr> for FormError {
111 fn from(err: sea_orm::DbErr) -> Self {
112 Self::Database(err.to_string())
113 }
114}
115
116impl From<evalexpr::EvalexprError> for FormError {
117 fn from(err: evalexpr::EvalexprError) -> Self {
118 Self::ConditionError(err.to_string())
119 }
120}
121
122pub trait IntoApiError {
147 type Response: IntoResponse;
148
149 fn into_api_error(error: FormError) -> Self::Response;
150}
151
152impl IntoApiError for FormError {
154 type Response = Self;
155
156 fn into_api_error(error: FormError) -> Self::Response {
157 error
158 }
159}
160
161#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
163pub struct ValidationErrors {
164 pub errors: HashMap<String, Vec<String>>,
166}
167
168#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
173pub struct StepValidationErrors {
174 pub steps: HashMap<String, HashMap<String, Vec<String>>>,
176}
177
178impl StepValidationErrors {
179 #[must_use]
181 pub fn new() -> Self {
182 Self::default()
183 }
184
185 #[must_use]
187 pub fn is_empty(&self) -> bool {
188 self.steps.is_empty() || self.steps.values().all(|fields| fields.is_empty())
189 }
190
191 #[must_use]
193 pub fn field_count(&self) -> usize {
194 self.steps.values().map(HashMap::len).sum()
195 }
196
197 #[must_use]
199 pub fn error_count(&self) -> usize {
200 self.steps
201 .values()
202 .flat_map(|fields| fields.values())
203 .map(Vec::len)
204 .sum()
205 }
206
207 pub fn add(
209 &mut self,
210 step_id: impl Into<String>,
211 field: impl Into<String>,
212 message: impl Into<String>,
213 ) {
214 self.steps
215 .entry(step_id.into())
216 .or_default()
217 .entry(field.into())
218 .or_default()
219 .push(message.into());
220 }
221
222 #[must_use]
224 pub fn get_step(&self, step_id: &str) -> Option<&HashMap<String, Vec<String>>> {
225 self.steps.get(step_id)
226 }
227
228 #[must_use]
230 pub fn get_field(&self, step_id: &str, field: &str) -> Option<&Vec<String>> {
231 self.steps.get(step_id).and_then(|s| s.get(field))
232 }
233
234 #[must_use]
236 pub fn flatten(&self) -> ValidationErrors {
237 let mut errors = ValidationErrors::new();
238 for fields in self.steps.values() {
239 for (field, messages) in fields {
240 for message in messages {
241 errors.add(field.clone(), message.clone());
242 }
243 }
244 }
245 errors
246 }
247}
248
249impl ValidationErrors {
250 #[must_use]
252 pub fn new() -> Self {
253 Self::default()
254 }
255
256 #[must_use]
258 pub fn is_empty(&self) -> bool {
259 self.errors.is_empty()
260 }
261
262 #[must_use]
264 pub fn len(&self) -> usize {
265 self.errors.len()
266 }
267
268 pub fn add(&mut self, field: impl Into<String>, message: impl Into<String>) {
270 self.errors
271 .entry(field.into())
272 .or_default()
273 .push(message.into());
274 }
275
276 #[must_use]
278 pub fn get(&self, field: &str) -> Option<&Vec<String>> {
279 self.errors.get(field)
280 }
281
282 pub fn merge(&mut self, other: Self) {
284 for (field, messages) in other.errors {
285 self.errors.entry(field).or_default().extend(messages);
286 }
287 }
288}
289
290impl std::fmt::Display for ValidationErrors {
291 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292 let count = self.errors.values().map(Vec::len).sum::<usize>();
293 write!(f, "{count} validation error(s)")
294 }
295}
296
297impl std::fmt::Display for StepValidationErrors {
298 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299 let count = self.error_count();
300 let step_count = self.steps.len();
301 write!(f, "{count} validation error(s) in {step_count} step(s)")
302 }
303}