forgedb_validation/
http.rs1use crate::ValidationError;
4
5#[derive(Debug, Clone)]
7pub struct HttpValidationError {
8 pub status_code: u16,
9 pub errors: Vec<ValidationError>,
10}
11
12impl HttpValidationError {
13 pub fn bad_request(errors: Vec<ValidationError>) -> Self {
15 Self {
16 status_code: 400,
17 errors,
18 }
19 }
20
21 pub fn not_found(message: impl Into<String>) -> Self {
23 Self {
24 status_code: 404,
25 errors: vec![ValidationError::new(message)],
26 }
27 }
28
29 pub fn conflict(message: impl Into<String>) -> Self {
31 Self {
32 status_code: 409,
33 errors: vec![ValidationError::new(message)],
34 }
35 }
36
37 pub fn unprocessable_entity(errors: Vec<ValidationError>) -> Self {
39 Self {
40 status_code: 422,
41 errors,
42 }
43 }
44
45 pub fn internal_error(message: impl Into<String>) -> Self {
47 Self {
48 status_code: 500,
49 errors: vec![ValidationError::new(message)],
50 }
51 }
52
53 pub fn is_client_error(&self) -> bool {
55 self.status_code >= 400 && self.status_code < 500
56 }
57
58 pub fn is_server_error(&self) -> bool {
60 self.status_code >= 500 && self.status_code < 600
61 }
62
63 pub fn message(&self) -> String {
65 if self.errors.is_empty() {
66 "Unknown error".to_string()
67 } else {
68 self.errors[0].message.clone()
69 }
70 }
71}
72
73impl std::fmt::Display for HttpValidationError {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "HTTP {} - {}", self.status_code, self.message())
76 }
77}
78
79impl std::error::Error for HttpValidationError {}
80
81pub struct HttpValidator;
83
84impl HttpValidator {
85 pub fn validate_required_fields(
87 fields: &[(&str, Option<&str>)],
88 ) -> Result<(), Vec<ValidationError>> {
89 let errors: Vec<ValidationError> = fields
90 .iter()
91 .filter_map(|(name, value)| {
92 if value.is_none() || value.unwrap().is_empty() {
93 Some(ValidationError::new(format!(
94 "Field '{}' is required",
95 name
96 )))
97 } else {
98 None
99 }
100 })
101 .collect();
102
103 if errors.is_empty() {
104 Ok(())
105 } else {
106 Err(errors)
107 }
108 }
109
110 pub fn validate_email(email: &str) -> Result<(), ValidationError> {
112 if email.contains('@') && email.contains('.') && email.len() >= 5 {
113 Ok(())
114 } else {
115 Err(ValidationError::new("Invalid email format")
116 .with_suggestion("Email must contain @ and domain"))
117 }
118 }
119
120 pub fn validate_length(
122 field_name: &str,
123 value: &str,
124 min: usize,
125 max: usize,
126 ) -> Result<(), ValidationError> {
127 let len = value.len();
128 if len < min {
129 Err(ValidationError::new(format!(
130 "Field '{}' must be at least {} characters",
131 field_name, min
132 )))
133 } else if len > max {
134 Err(ValidationError::new(format!(
135 "Field '{}' must be at most {} characters",
136 field_name, max
137 )))
138 } else {
139 Ok(())
140 }
141 }
142
143 pub fn validate_range<T: PartialOrd + std::fmt::Display>(
145 field_name: &str,
146 value: T,
147 min: T,
148 max: T,
149 ) -> Result<(), ValidationError> {
150 if value < min {
151 Err(ValidationError::new(format!(
152 "Field '{}' must be at least {}",
153 field_name, min
154 )))
155 } else if value > max {
156 Err(ValidationError::new(format!(
157 "Field '{}' must be at most {}",
158 field_name, max
159 )))
160 } else {
161 Ok(())
162 }
163 }
164}
165