1use super::calculations::calculate_bandwidth_mbps;
4use super::formatting::sanitize_tag;
5use super::time::now_ms;
6
7#[inline]
25pub fn is_valid_email(email: &str) -> bool {
26 let parts: Vec<&str> = email.split('@').collect();
27 if parts.len() != 2 {
28 return false;
29 }
30
31 let local = parts[0];
32 let domain = parts[1];
33
34 !local.is_empty() && !domain.is_empty() && domain.contains('.')
35}
36
37#[inline]
57pub fn is_valid_username(username: &str) -> bool {
58 if username.len() < 3 || username.len() > 20 {
59 return false;
60 }
61
62 username
63 .chars()
64 .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
65}
66
67#[inline]
86pub fn is_valid_cid(cid: &str) -> bool {
87 if cid.is_empty() {
89 return false;
90 }
91
92 let valid_prefix = cid.starts_with("Qm") || cid.starts_with("bafy") || cid.starts_with("bafk");
93 let valid_chars = cid.chars().all(|c| c.is_alphanumeric());
94
95 valid_prefix && valid_chars && cid.len() >= 46
96}
97
98#[inline]
115pub fn is_valid_peer_id(peer_id: &str) -> bool {
116 peer_id.starts_with("12D3Koo") && peer_id.len() >= 46
118}
119
120#[inline]
122pub fn is_safe_string(s: &str) -> bool {
123 s.chars().all(|c| !c.is_control() || c.is_whitespace())
124}
125
126pub fn validate_cids_batch(cids: &[String]) -> Vec<String> {
128 cids.iter()
129 .filter(|cid| !is_valid_cid(cid))
130 .cloned()
131 .collect()
132}
133
134pub fn validate_emails_batch(emails: &[String]) -> Vec<String> {
136 emails
137 .iter()
138 .filter(|email| !is_valid_email(email))
139 .cloned()
140 .collect()
141}
142
143pub fn validate_usernames_batch(usernames: &[String]) -> Vec<String> {
145 usernames
146 .iter()
147 .filter(|username| !is_valid_username(username))
148 .cloned()
149 .collect()
150}
151
152pub fn is_valid_ipv4(ip: &str) -> bool {
173 let parts: Vec<&str> = ip.split('.').collect();
174 if parts.len() != 4 {
175 return false;
176 }
177
178 parts.iter().all(|part| part.parse::<u8>().is_ok())
179}
180
181pub fn is_valid_ipv6(ip: &str) -> bool {
183 if !ip.contains(':') {
185 return false;
186 }
187
188 let parts: Vec<&str> = ip.split(':').collect();
189 if parts.len() < 3 || parts.len() > 8 {
190 return false;
191 }
192
193 parts
194 .iter()
195 .all(|part| part.is_empty() || part.chars().all(|c| c.is_ascii_hexdigit()))
196}
197
198pub fn is_valid_port(port: u16) -> bool {
200 port > 0
201}
202
203pub fn is_valid_multiaddr(addr: &str) -> bool {
205 if !addr.starts_with('/') {
207 return false;
208 }
209
210 let parts: Vec<&str> = addr.split('/').filter(|s| !s.is_empty()).collect();
211 if parts.is_empty() {
212 return false;
213 }
214
215 let valid_protocols = [
217 "ip4", "ip6", "tcp", "udp", "quic", "p2p", "ws", "wss", "http", "https",
218 ];
219 parts.iter().any(|part| valid_protocols.contains(part))
220}
221
222pub fn is_valid_url(url: &str) -> bool {
240 if url.is_empty() || url.len() > 2048 {
241 return false;
242 }
243
244 url.starts_with("http://") || url.starts_with("https://")
245}
246
247pub fn is_valid_hex(hex: &str) -> bool {
249 hex.len() % 2 == 0 && hex.chars().all(|c| c.is_ascii_hexdigit())
250}
251
252pub fn validate_chunk_size(size: usize, min: usize, max: usize) -> bool {
254 size >= min && size <= max
255}
256
257pub fn validate_proof_freshness(timestamp_ms: i64, tolerance_ms: i64) -> bool {
259 let now = now_ms();
260 let age = now - timestamp_ms;
261 age >= 0 && age <= tolerance_ms
262}
263
264pub fn validate_latency(latency_ms: u32, max_latency_ms: u32) -> bool {
266 latency_ms <= max_latency_ms
267}
268
269pub fn validate_bandwidth_reasonable(bytes: u64, duration_ms: u64, max_mbps: f64) -> bool {
271 if duration_ms == 0 {
272 return false;
273 }
274
275 let mbps = calculate_bandwidth_mbps(bytes, duration_ms);
276 mbps <= max_mbps
277}
278
279pub fn validate_nonce_length(nonce: &[u8], expected_len: usize) -> bool {
281 nonce.len() == expected_len
282}
283
284pub fn validate_signature_length(signature: &[u8], expected_len: usize) -> bool {
286 signature.len() == expected_len
287}
288
289pub fn validate_public_key_length(public_key: &[u8], expected_len: usize) -> bool {
291 public_key.len() == expected_len
292}
293
294pub fn validate_hash_length(hash: &[u8], expected_len: usize) -> bool {
296 hash.len() == expected_len
297}
298
299pub fn validate_chunk_indices_batch(indices: &[u64], max_index: u64) -> Vec<u64> {
301 indices
302 .iter()
303 .filter(|&&idx| idx >= max_index)
304 .copied()
305 .collect()
306}
307
308pub fn validate_content_size_in_range(size: u64, min: u64, max: u64) -> bool {
310 size >= min && size <= max
311}
312
313pub fn validate_price_range(price: u64, min: u64, max: u64) -> bool {
315 price >= min && price <= max
316}
317
318pub fn validate_and_sanitize_tag(tag: &str, max_len: usize) -> Option<String> {
320 let sanitized = sanitize_tag(tag);
321 if sanitized.is_empty() || sanitized.len() > max_len {
322 None
323 } else {
324 Some(sanitized)
325 }
326}
327
328pub fn validate_tags_list(tags: &[String], max_len: usize, max_count: usize) -> Vec<String> {
330 tags.iter()
331 .take(max_count)
332 .filter_map(|t| validate_and_sanitize_tag(t, max_len))
333 .collect()
334}
335
336pub fn validate_ed25519_signature(signature: &[u8]) -> bool {
338 validate_signature_length(signature, 64)
339}
340
341pub fn validate_ed25519_public_key(public_key: &[u8]) -> bool {
343 validate_public_key_length(public_key, 32)
344}
345
346pub fn validate_blake3_hash(hash: &[u8]) -> bool {
348 validate_hash_length(hash, 32)
349}
350
351pub fn validate_challenge_nonce(nonce: &[u8]) -> bool {
353 validate_nonce_length(nonce, 32)
354}
355
356pub fn is_private_ipv4(ip: &str) -> bool {
359 let parts: Vec<&str> = ip.split('.').collect();
360 if parts.len() != 4 {
361 return false;
362 }
363
364 let octets: Result<Vec<u8>, _> = parts.iter().map(|s| s.parse()).collect();
365 if let Ok(octets) = octets {
366 if octets.len() != 4 {
367 return false;
368 }
369
370 if octets[0] == 10 {
372 return true;
373 }
374
375 if octets[0] == 172 && (16..=31).contains(&octets[1]) {
377 return true;
378 }
379
380 if octets[0] == 192 && octets[1] == 168 {
382 return true;
383 }
384
385 if octets[0] == 127 {
387 return true;
388 }
389
390 false
391 } else {
392 false
393 }
394}
395
396#[must_use]
398#[allow(dead_code)]
399pub fn validate_url_safe_string(s: &str) -> bool {
400 !s.is_empty()
401 && s.chars()
402 .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
403}
404
405#[must_use]
407#[allow(dead_code)]
408pub fn validate_json_string(s: &str) -> bool {
409 serde_json::from_str::<serde_json::Value>(s).is_ok()
410}
411
412#[must_use]
414#[allow(dead_code)]
415pub fn validate_semver(version: &str) -> bool {
416 let main_prerelease: Vec<&str> = version.splitn(2, '-').collect();
418 let main_version = main_prerelease[0];
419
420 let parts: Vec<&str> = main_version.split('.').collect();
422 if parts.len() != 3 {
423 return false;
424 }
425
426 if !parts
428 .iter()
429 .all(|part| !part.is_empty() && part.chars().all(|c| c.is_ascii_digit()))
430 {
431 return false;
432 }
433
434 if main_prerelease.len() > 1 {
436 let prerelease = main_prerelease[1];
437 !prerelease.is_empty()
438 && prerelease
439 .chars()
440 .all(|c| c.is_alphanumeric() || c == '.' || c == '-')
441 } else {
442 true
443 }
444}
445
446#[must_use]
448#[allow(dead_code)]
449pub fn validate_uuid_v4(uuid: &str) -> bool {
450 if uuid.len() != 36 {
451 return false;
452 }
453
454 let parts: Vec<&str> = uuid.split('-').collect();
455 if parts.len() != 5 {
456 return false;
457 }
458
459 if parts[0].len() != 8
461 || parts[1].len() != 4
462 || parts[2].len() != 4
463 || parts[3].len() != 4
464 || parts[4].len() != 12
465 {
466 return false;
467 }
468
469 if !parts[2].starts_with('4') {
471 return false;
472 }
473
474 parts
476 .iter()
477 .all(|part| part.chars().all(|c| c.is_ascii_hexdigit()))
478}
479
480#[must_use]
482#[allow(dead_code)]
483pub fn validate_hex_color(color: &str) -> bool {
484 if !color.starts_with('#') {
485 return false;
486 }
487
488 let hex = &color[1..];
489 (hex.len() == 3 || hex.len() == 6) && hex.chars().all(|c| c.is_ascii_hexdigit())
490}
491
492#[must_use]
494#[allow(dead_code)]
495pub fn validate_port_range(port: u16) -> bool {
496 port > 0
497}
498
499#[must_use]
501#[allow(dead_code)]
502pub fn validate_content_type(content_type: &str) -> bool {
503 let parts: Vec<&str> = content_type.split('/').collect();
504 if parts.len() != 2 {
505 return false;
506 }
507
508 let type_valid = !parts[0].is_empty()
509 && parts[0]
510 .chars()
511 .all(|c| c.is_alphanumeric() || c == '-' || c == '+');
512
513 let subtype = parts[1].split(';').next().unwrap_or("");
514 let subtype_valid = !subtype.is_empty()
515 && subtype
516 .chars()
517 .all(|c| c.is_alphanumeric() || c == '-' || c == '+' || c == '.');
518
519 type_valid && subtype_valid
520}
521
522#[must_use]
524#[allow(dead_code)]
525pub fn validate_printable_ascii(s: &str) -> bool {
526 s.chars().all(|c| c.is_ascii() && !c.is_ascii_control())
527}
528
529#[must_use]
531#[allow(dead_code)]
532pub fn validate_base64(s: &str) -> bool {
533 if s.is_empty() || s.len() % 4 != 0 {
534 return false;
535 }
536
537 let bytes = s.as_bytes();
538 let len = bytes.len();
539
540 for (i, &byte) in bytes.iter().enumerate() {
541 let c = byte as char;
542 if c == '=' {
543 if i != len - 1 && i != len - 2 {
545 return false;
546 }
547 if i == len - 2 && bytes[len - 1] != b'=' {
549 return false;
550 }
551 } else if !c.is_alphanumeric() && c != '+' && c != '/' {
552 return false;
553 }
554 }
555
556 true
557}
558
559#[cfg(test)]
560mod tests {
561 use super::*;
562
563 #[test]
564 fn test_is_valid_email() {
565 assert!(is_valid_email("test@example.com"));
566 assert!(is_valid_email("user+tag@domain.co.jp"));
567 assert!(!is_valid_email("invalid"));
568 assert!(!is_valid_email("@example.com"));
569 assert!(!is_valid_email("test@"));
570 assert!(!is_valid_email("test@com"));
571 }
572
573 #[test]
574 fn test_is_valid_username() {
575 assert!(is_valid_username("alice"));
576 assert!(is_valid_username("alice_123"));
577 assert!(is_valid_username("alice-bob"));
578 assert!(!is_valid_username("ab"));
579 assert!(!is_valid_username(&"a".repeat(21)));
580 assert!(!is_valid_username("alice bob"));
581 assert!(!is_valid_username("alice@bob"));
582 }
583
584 #[test]
585 fn test_is_valid_cid() {
586 assert!(is_valid_cid(
587 "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG"
588 ));
589 assert!(is_valid_cid(
590 "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"
591 ));
592 assert!(!is_valid_cid("invalid"));
593 assert!(!is_valid_cid(""));
594 assert!(!is_valid_cid("Qm"));
595 }
596
597 #[test]
598 fn test_is_valid_peer_id() {
599 assert!(is_valid_peer_id(
600 "12D3KooWRQTwRLgXZfYJnAL8fCF7VBvPgPDJxz3cBdJq9BvZT8bT"
601 ));
602 assert!(!is_valid_peer_id("invalid"));
603 assert!(!is_valid_peer_id("12D3Koo"));
604 }
605
606 #[test]
607 fn test_is_safe_string() {
608 assert!(is_safe_string("hello world"));
609 assert!(is_safe_string("hello\nworld"));
610 assert!(is_safe_string("hello\tworld"));
611 assert!(!is_safe_string("hello\x00world"));
612 assert!(!is_safe_string("hello\x01world"));
613 }
614
615 #[test]
616 fn test_validate_cids_batch() {
617 let cids = vec![
618 "QmValidCID1234567890123456789012345678901234567890".to_string(),
619 "invalid".to_string(),
620 "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi".to_string(),
621 "".to_string(),
622 ];
623 let invalid = validate_cids_batch(&cids);
624 assert_eq!(invalid.len(), 2);
625 assert!(invalid.contains(&"invalid".to_string()));
626 assert!(invalid.contains(&"".to_string()));
627 }
628
629 #[test]
630 fn test_validate_emails_batch() {
631 let emails = vec![
632 "valid@example.com".to_string(),
633 "invalid".to_string(),
634 "another@test.org".to_string(),
635 "@invalid.com".to_string(),
636 ];
637 let invalid = validate_emails_batch(&emails);
638 assert_eq!(invalid.len(), 2);
639 }
640
641 #[test]
642 fn test_validate_usernames_batch() {
643 let usernames = vec![
644 "validuser123".to_string(),
645 "ab".to_string(), "user_name".to_string(),
647 "a".repeat(25), ];
649 let invalid = validate_usernames_batch(&usernames);
650 assert_eq!(invalid.len(), 2);
651 }
652
653 #[test]
654 fn test_is_valid_ipv4() {
655 assert!(is_valid_ipv4("192.168.1.1"));
656 assert!(is_valid_ipv4("127.0.0.1"));
657 assert!(is_valid_ipv4("255.255.255.255"));
658 assert!(!is_valid_ipv4("256.1.1.1"));
659 assert!(!is_valid_ipv4("192.168.1"));
660 assert!(!is_valid_ipv4("192.168.1.1.1"));
661 assert!(!is_valid_ipv4("not.an.ip.addr"));
662 }
663
664 #[test]
665 fn test_is_valid_ipv6() {
666 assert!(is_valid_ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334"));
667 assert!(is_valid_ipv6("2001:db8::1"));
668 assert!(is_valid_ipv6("::1"));
669 assert!(!is_valid_ipv6("192.168.1.1"));
670 assert!(!is_valid_ipv6("not:valid:ipv6:xyz"));
671 assert!(!is_valid_ipv6(""));
672 }
673
674 #[test]
675 fn test_is_valid_port() {
676 assert!(is_valid_port(80));
677 assert!(is_valid_port(443));
678 assert!(is_valid_port(65535));
679 assert!(!is_valid_port(0));
680 }
681
682 #[test]
683 fn test_is_valid_multiaddr() {
684 assert!(is_valid_multiaddr("/ip4/127.0.0.1/tcp/4001"));
685 assert!(is_valid_multiaddr("/ip6/::1/tcp/4001"));
686 assert!(is_valid_multiaddr("/ip4/192.168.1.1/tcp/8080/ws"));
687 assert!(!is_valid_multiaddr("invalid"));
688 assert!(!is_valid_multiaddr(""));
689 assert!(!is_valid_multiaddr("/invalid/address"));
690 }
691
692 #[test]
693 fn test_is_valid_url() {
694 assert!(is_valid_url("https://example.com"));
695 assert!(is_valid_url("http://example.com"));
696 assert!(is_valid_url("https://example.com/path?query=value"));
697 assert!(!is_valid_url("ftp://example.com"));
698 assert!(!is_valid_url(""));
699 assert!(!is_valid_url("not a url"));
700 let long_url = format!("https://{}", "a".repeat(3000));
702 assert!(!is_valid_url(&long_url));
703 }
704
705 #[test]
706 fn test_is_valid_hex() {
707 assert!(is_valid_hex("deadbeef"));
708 assert!(is_valid_hex("0123456789abcdef"));
709 assert!(is_valid_hex(""));
710 assert!(!is_valid_hex("abc")); assert!(!is_valid_hex("xyz")); }
713
714 #[test]
715 fn test_validate_chunk_size() {
716 assert!(validate_chunk_size(1024, 512, 2048));
717 assert!(!validate_chunk_size(256, 512, 2048));
718 assert!(!validate_chunk_size(4096, 512, 2048));
719 }
720
721 #[test]
722 fn test_validate_proof_freshness() {
723 let now = now_ms();
724 assert!(validate_proof_freshness(now, 5000));
725 assert!(validate_proof_freshness(now - 1000, 5000));
726 assert!(!validate_proof_freshness(now - 10000, 5000));
727 assert!(!validate_proof_freshness(now + 1000, 5000));
728 }
729
730 #[test]
731 fn test_validate_latency() {
732 assert!(validate_latency(100, 500));
733 assert!(validate_latency(500, 500));
734 assert!(!validate_latency(501, 500));
735 }
736
737 #[test]
738 fn test_validate_bandwidth_reasonable() {
739 assert!(validate_bandwidth_reasonable(100_000_000, 10_000, 1000.0));
741
742 assert!(!validate_bandwidth_reasonable(
744 10_000_000_000,
745 1_000,
746 1000.0
747 ));
748
749 assert!(!validate_bandwidth_reasonable(1000, 0, 1000.0));
751 }
752
753 #[test]
754 fn test_validate_nonce_length() {
755 assert!(validate_nonce_length(&[0u8; 32], 32));
756 assert!(!validate_nonce_length(&[0u8; 16], 32));
757 assert!(!validate_nonce_length(&[0u8; 64], 32));
758 }
759
760 #[test]
761 fn test_validate_signature_length() {
762 assert!(validate_signature_length(&[0u8; 64], 64));
763 assert!(!validate_signature_length(&[0u8; 32], 64));
764 }
765
766 #[test]
767 fn test_validate_public_key_length() {
768 assert!(validate_public_key_length(&[0u8; 32], 32));
769 assert!(!validate_public_key_length(&[0u8; 64], 32));
770 }
771
772 #[test]
773 fn test_validate_hash_length() {
774 assert!(validate_hash_length(&[0u8; 32], 32));
775 assert!(!validate_hash_length(&[0u8; 16], 32));
776 }
777
778 #[test]
779 fn test_validate_chunk_indices_batch() {
780 let indices = vec![0, 1, 5, 10, 15];
781 let invalid = validate_chunk_indices_batch(&indices, 10);
782 assert_eq!(invalid, vec![10, 15]);
783
784 let all_valid = validate_chunk_indices_batch(&indices, 20);
785 assert!(all_valid.is_empty());
786 }
787
788 #[test]
789 fn test_validate_content_size_in_range() {
790 assert!(validate_content_size_in_range(1024, 512, 2048));
791 assert!(validate_content_size_in_range(512, 512, 2048));
792 assert!(validate_content_size_in_range(2048, 512, 2048));
793 assert!(!validate_content_size_in_range(256, 512, 2048));
794 assert!(!validate_content_size_in_range(4096, 512, 2048));
795 }
796
797 #[test]
798 fn test_validate_price_range() {
799 assert!(validate_price_range(100, 1, 1000));
800 assert!(!validate_price_range(0, 1, 1000));
801 assert!(!validate_price_range(1001, 1, 1000));
802 }
803
804 #[test]
805 fn test_validate_and_sanitize_tag() {
806 assert_eq!(
807 validate_and_sanitize_tag(" Rust ", 20),
808 Some("rust".to_string())
809 );
810 assert_eq!(validate_and_sanitize_tag(" ", 20), None);
811 assert_eq!(validate_and_sanitize_tag("verylongtagname", 10), None);
812 }
813
814 #[test]
815 fn test_validate_tags_list() {
816 let tags = vec![
817 "Rust".to_string(),
818 " Python ".to_string(),
819 " ".to_string(),
820 "JavaScript".to_string(),
821 "Go".to_string(),
822 ];
823
824 let valid = validate_tags_list(&tags, 20, 3);
827 assert_eq!(valid, vec!["rust", "python"]);
832
833 let limited = validate_tags_list(&tags, 20, 2);
834 assert_eq!(limited.len(), 2);
835 }
836
837 #[test]
838 fn test_validate_ed25519_signature() {
839 assert!(validate_ed25519_signature(&[0u8; 64]));
840 assert!(!validate_ed25519_signature(&[0u8; 32]));
841 }
842
843 #[test]
844 fn test_validate_ed25519_public_key() {
845 assert!(validate_ed25519_public_key(&[0u8; 32]));
846 assert!(!validate_ed25519_public_key(&[0u8; 64]));
847 }
848
849 #[test]
850 fn test_validate_blake3_hash() {
851 assert!(validate_blake3_hash(&[0u8; 32]));
852 assert!(!validate_blake3_hash(&[0u8; 16]));
853 }
854
855 #[test]
856 fn test_validate_challenge_nonce() {
857 assert!(validate_challenge_nonce(&[0u8; 32]));
858 assert!(!validate_challenge_nonce(&[0u8; 16]));
859 }
860
861 #[test]
862 fn test_is_private_ipv4() {
863 assert!(is_private_ipv4("10.0.0.1"));
864 assert!(is_private_ipv4("10.255.255.255"));
865 assert!(is_private_ipv4("172.16.0.1"));
866 assert!(is_private_ipv4("172.31.255.255"));
867 assert!(is_private_ipv4("192.168.1.1"));
868 assert!(is_private_ipv4("192.168.255.255"));
869 assert!(is_private_ipv4("127.0.0.1"));
870 assert!(!is_private_ipv4("8.8.8.8"));
871 assert!(!is_private_ipv4("1.2.3.4"));
872 assert!(!is_private_ipv4("172.15.0.1")); assert!(!is_private_ipv4("172.32.0.1")); assert!(!is_private_ipv4("invalid"));
875 }
876
877 #[test]
879 fn test_validate_url_safe_string() {
880 assert!(validate_url_safe_string("hello-world_123"));
881 assert!(validate_url_safe_string("test_slug"));
882 assert!(validate_url_safe_string("abc-123"));
883 assert!(!validate_url_safe_string(""));
884 assert!(!validate_url_safe_string("hello world")); assert!(!validate_url_safe_string("test@example")); }
887
888 #[test]
889 fn test_validate_json_string() {
890 assert!(validate_json_string(r#"{"key": "value"}"#));
891 assert!(validate_json_string(r"[1, 2, 3]"));
892 assert!(validate_json_string(r"null"));
893 assert!(validate_json_string(r"true"));
894 assert!(validate_json_string(r"42"));
895 assert!(!validate_json_string(r"{invalid}"));
896 assert!(!validate_json_string(r"not json"));
897 }
898
899 #[test]
900 fn test_validate_semver() {
901 assert!(validate_semver("1.0.0"));
902 assert!(validate_semver("1.2.3"));
903 assert!(validate_semver("0.0.1"));
904 assert!(validate_semver("1.0.0-beta"));
905 assert!(validate_semver("2.1.3-alpha.1"));
906 assert!(!validate_semver("1.0")); assert!(!validate_semver("1.0.0.0")); assert!(!validate_semver("a.b.c")); assert!(!validate_semver("1.2.")); }
911
912 #[test]
913 fn test_validate_uuid_v4() {
914 assert!(validate_uuid_v4("550e8400-e29b-41d4-a716-446655440000"));
915 assert!(validate_uuid_v4("f47ac10b-58cc-4372-a567-0e02b2c3d479"));
916 assert!(!validate_uuid_v4("550e8400-e29b-31d4-a716-446655440000")); assert!(!validate_uuid_v4("550e8400-e29b-41d4-a716")); assert!(!validate_uuid_v4("not-a-uuid"));
919 assert!(!validate_uuid_v4("550e8400e29b41d4a716446655440000")); }
921
922 #[test]
923 fn test_validate_hex_color() {
924 assert!(validate_hex_color("#FFF"));
925 assert!(validate_hex_color("#fff"));
926 assert!(validate_hex_color("#FFFFFF"));
927 assert!(validate_hex_color("#123abc"));
928 assert!(!validate_hex_color("FFF")); assert!(!validate_hex_color("#FF")); assert!(!validate_hex_color("#FFFFFFF")); assert!(!validate_hex_color("#GGG")); }
933
934 #[test]
935 fn test_validate_port_range() {
936 assert!(validate_port_range(1));
937 assert!(validate_port_range(80));
938 assert!(validate_port_range(8080));
939 assert!(validate_port_range(65535));
940 assert!(!validate_port_range(0));
941 }
942
943 #[test]
944 fn test_validate_content_type() {
945 assert!(validate_content_type("text/plain"));
946 assert!(validate_content_type("application/json"));
947 assert!(validate_content_type("image/png"));
948 assert!(validate_content_type("text/html; charset=utf-8"));
949 assert!(validate_content_type("application/vnd.api+json"));
950 assert!(!validate_content_type("invalid"));
951 assert!(!validate_content_type("/json")); assert!(!validate_content_type("text/")); }
954
955 #[test]
956 fn test_validate_printable_ascii() {
957 assert!(validate_printable_ascii("Hello World 123"));
958 assert!(validate_printable_ascii("test@example.com"));
959 assert!(!validate_printable_ascii("hello\nworld")); assert!(!validate_printable_ascii("test\0null")); assert!(!validate_printable_ascii("hello\tworld")); }
963
964 #[test]
965 fn test_validate_base64() {
966 assert!(validate_base64("SGVsbG8=")); assert!(validate_base64("YWJjMTIz")); assert!(validate_base64("dGVzdA==")); assert!(!validate_base64("invalid!")); assert!(!validate_base64("abc")); assert!(!validate_base64("ab=c")); }
973}