1use super::core::*;
4use std::fmt;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum ValidationError {
9 InvalidPublicKeyLength { expected: usize, actual: usize },
11 InvalidSignatureLength { expected: usize, actual: usize },
13 InvalidNonceLength { expected: usize, actual: usize },
15 InvalidHashLength { expected: usize, actual: usize },
17 TimestampInFuture { timestamp_ms: i64, now_ms: i64 },
19 TimestampTooOld {
21 timestamp_ms: i64,
22 now_ms: i64,
23 tolerance_ms: i64,
24 },
25 InvalidTimestampOrder { start_ms: i64, end_ms: i64 },
27 LatencyTooLow { latency_ms: u32, min_ms: u32 },
29 LatencyTooHigh { latency_ms: u32, max_ms: u32 },
31 LatencyMismatch {
33 calculated_ms: i64,
34 reported_ms: u32,
35 },
36 BytesExceedMax { bytes: u64, max: u64 },
38 SelfTransfer,
40 EmptyCid,
42 ContentSizeOutOfBounds { size: u64, min: u64, max: u64 },
44 TitleTooLong { length: usize, max: usize },
46 DescriptionTooLong { length: usize, max: usize },
48 TooManyTags { count: usize, max: usize },
50 TagTooLong {
52 tag: String,
53 length: usize,
54 max: usize,
55 },
56}
57
58impl fmt::Display for ValidationError {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 match self {
61 Self::InvalidPublicKeyLength { expected, actual } => {
62 write!(
63 f,
64 "Invalid public key length: expected {expected}, got {actual}"
65 )
66 }
67 Self::InvalidSignatureLength { expected, actual } => {
68 write!(
69 f,
70 "Invalid signature length: expected {expected}, got {actual}"
71 )
72 }
73 Self::InvalidNonceLength { expected, actual } => {
74 write!(f, "Invalid nonce length: expected {expected}, got {actual}")
75 }
76 Self::InvalidHashLength { expected, actual } => {
77 write!(f, "Invalid hash length: expected {expected}, got {actual}")
78 }
79 Self::TimestampInFuture {
80 timestamp_ms,
81 now_ms,
82 } => {
83 write!(
84 f,
85 "Timestamp {timestamp_ms} is in the future (now: {now_ms})"
86 )
87 }
88 Self::TimestampTooOld {
89 timestamp_ms,
90 now_ms,
91 tolerance_ms,
92 } => {
93 write!(
94 f,
95 "Timestamp {timestamp_ms} is too old (now: {now_ms}, tolerance: {tolerance_ms}ms)"
96 )
97 }
98 Self::InvalidTimestampOrder { start_ms, end_ms } => {
99 write!(
100 f,
101 "Start timestamp {start_ms} is after end timestamp {end_ms}"
102 )
103 }
104 Self::LatencyTooLow { latency_ms, min_ms } => {
105 write!(f, "Latency {latency_ms}ms is below minimum {min_ms}ms")
106 }
107 Self::LatencyTooHigh { latency_ms, max_ms } => {
108 write!(f, "Latency {latency_ms}ms exceeds maximum {max_ms}ms")
109 }
110 Self::LatencyMismatch {
111 calculated_ms,
112 reported_ms,
113 } => {
114 write!(
115 f,
116 "Reported latency {reported_ms}ms doesn't match calculated {calculated_ms}ms"
117 )
118 }
119 Self::BytesExceedMax { bytes, max } => {
120 write!(f, "Bytes transferred {bytes} exceeds maximum {max}")
121 }
122 Self::SelfTransfer => write!(f, "Provider and requester cannot be the same"),
123 Self::EmptyCid => write!(f, "Content CID cannot be empty"),
124 Self::ContentSizeOutOfBounds { size, min, max } => {
125 write!(f, "Content size {size} is out of bounds [{min}, {max}]")
126 }
127 Self::TitleTooLong { length, max } => {
128 write!(f, "Title length {length} exceeds maximum {max}")
129 }
130 Self::DescriptionTooLong { length, max } => {
131 write!(f, "Description length {length} exceeds maximum {max}")
132 }
133 Self::TooManyTags { count, max } => {
134 write!(f, "Tag count {count} exceeds maximum {max}")
135 }
136 Self::TagTooLong { tag, length, max } => {
137 write!(f, "Tag '{tag}' length {length} exceeds maximum {max}")
138 }
139 }
140 }
141}
142
143impl std::error::Error for ValidationError {}
144
145pub mod helpers {
147 use super::*;
148
149 pub fn validate_peer_id(peer_id: &str) -> Result<(), ValidationError> {
155 if peer_id.is_empty() {
156 return Err(ValidationError::EmptyCid); }
158 if !crate::utils::is_valid_peer_id(peer_id) {
159 return Err(ValidationError::EmptyCid); }
161 Ok(())
162 }
163
164 pub fn validate_cid(cid: &str) -> Result<(), ValidationError> {
170 if cid.is_empty() {
171 return Err(ValidationError::EmptyCid);
172 }
173 if !crate::utils::is_valid_cid(cid) {
174 return Err(ValidationError::EmptyCid);
175 }
176 Ok(())
177 }
178
179 pub fn validate_tag(tag: &str) -> Result<(), ValidationError> {
185 if tag.len() > MAX_TAG_LENGTH {
186 return Err(ValidationError::TagTooLong {
187 tag: tag.to_string(),
188 length: tag.len(),
189 max: MAX_TAG_LENGTH,
190 });
191 }
192 Ok(())
193 }
194
195 pub fn validate_tags(tags: &[String]) -> Result<(), Vec<ValidationError>> {
201 let mut errors = Vec::new();
202
203 if tags.len() > MAX_TAGS_COUNT {
204 errors.push(ValidationError::TooManyTags {
205 count: tags.len(),
206 max: MAX_TAGS_COUNT,
207 });
208 }
209
210 for tag in tags {
211 if let Err(e) = validate_tag(tag) {
212 errors.push(e);
213 }
214 }
215
216 if errors.is_empty() {
217 Ok(())
218 } else {
219 Err(errors)
220 }
221 }
222
223 pub fn validate_content_size(size: u64) -> Result<(), ValidationError> {
229 if !(MIN_CONTENT_SIZE..=MAX_CONTENT_SIZE).contains(&size) {
230 return Err(ValidationError::ContentSizeOutOfBounds {
231 size,
232 min: MIN_CONTENT_SIZE,
233 max: MAX_CONTENT_SIZE,
234 });
235 }
236 Ok(())
237 }
238
239 pub fn validate_title(title: &str) -> Result<(), ValidationError> {
245 if title.len() > MAX_TITLE_LENGTH {
246 return Err(ValidationError::TitleTooLong {
247 length: title.len(),
248 max: MAX_TITLE_LENGTH,
249 });
250 }
251 Ok(())
252 }
253
254 pub fn validate_description(description: &str) -> Result<(), ValidationError> {
260 if description.len() > MAX_DESCRIPTION_LENGTH {
261 return Err(ValidationError::DescriptionTooLong {
262 length: description.len(),
263 max: MAX_DESCRIPTION_LENGTH,
264 });
265 }
266 Ok(())
267 }
268
269 pub fn validate_all<T, F>(items: &[T], validator: F) -> Result<(), Vec<ValidationError>>
271 where
272 F: Fn(&T) -> Result<(), ValidationError>,
273 {
274 let errors: Vec<ValidationError> = items
275 .iter()
276 .filter_map(|item| validator(item).err())
277 .collect();
278
279 if errors.is_empty() {
280 Ok(())
281 } else {
282 Err(errors)
283 }
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_validation_error_display() {
293 let err = ValidationError::InvalidPublicKeyLength {
294 expected: 32,
295 actual: 16,
296 };
297 assert!(err.to_string().contains("Invalid public key length"));
298
299 let err = ValidationError::SelfTransfer;
300 assert!(err.to_string().contains("cannot be the same"));
301
302 let err = ValidationError::EmptyCid;
303 assert!(err.to_string().contains("cannot be empty"));
304 }
305}