sps_net/validation.rs
1// sps-io/src/checksum.rs
2//use std::sync::Arc;
3use std::fs::File;
4use std::io;
5use std::path::Path;
6
7use infer;
8use sha2::{Digest, Sha256};
9use sps_common::error::{Result, SpsError};
10use url::Url;
11//use tokio::fs::File;
12//use tokio::io::AsyncReadExt;
13//use tracing::debug; // Use tracing
14
15///// Asynchronously verifies the SHA256 checksum of a file.
16///// Reads the file asynchronously but performs hashing synchronously.
17//pub async fn verify_checksum_async(path: &Path, expected: &str) -> Result<()> {
18//debug!("Async Verifying checksum for: {}", path.display());
19// let file = File::open(path).await;
20// let mut file = match file {
21// Ok(f) => f,
22// Err(e) => {
23// return Err(SpsError::Io(Arc::new(e))); // Wrap IO error
24// }
25// };
26//
27// let mut hasher = Sha256::new();
28// let mut buffer = Vec::with_capacity(8192); // Use a Vec as buffer for read_buf
29// let mut total_bytes_read = 0;
30//
31// loop {
32// buffer.clear();
33// match file.read_buf(&mut buffer).await {
34// Ok(0) => break, // End of file
35// Ok(n) => {
36// hasher.update(&buffer[..n]);
37// total_bytes_read += n as u64;
38// }
39// Err(e) => {
40// return Err(SpsError::Io(Arc::new(e))); // Wrap IO error
41// }
42// }
43// }
44//
45// let hash_bytes = hasher.finalize();
46// let actual = hex::encode(hash_bytes);
47//
48// debug!(
49// "Async Calculated SHA256: {} ({} bytes read)",
50// actual, total_bytes_read
51// );
52// debug!("Expected SHA256: {}", expected);
53//
54// if actual.eq_ignore_ascii_case(expected) {
55// Ok(())
56// } else {
57// Err(SpsError::ChecksumError(format!(
58// "Checksum mismatch for {}: expected {}, got {}",
59// path.display(),
60// expected,
61// actual
62// )))
63// }
64//}
65
66// Keep the synchronous version for now if needed elsewhere or for comparison
67pub fn verify_checksum(path: &Path, expected: &str) -> Result<()> {
68 tracing::debug!("Verifying checksum for: {}", path.display());
69 let mut file = File::open(path)?;
70 let mut hasher = Sha256::new();
71 let bytes_copied = io::copy(&mut file, &mut hasher)?;
72 let hash_bytes = hasher.finalize();
73 let actual = hex::encode(hash_bytes);
74 tracing::debug!(
75 "Calculated SHA256: {} ({} bytes read)",
76 actual,
77 bytes_copied
78 );
79 tracing::debug!("Expected SHA256: {}", expected);
80 if actual.eq_ignore_ascii_case(expected) {
81 Ok(())
82 } else {
83 Err(SpsError::ChecksumError(format!(
84 "Checksum mismatch for {}: expected {}, got {}",
85 path.display(),
86 expected,
87 actual
88 )))
89 }
90}
91
92/// Verifies that the detected content type of the file matches the expected extension.
93pub fn verify_content_type(path: &Path, expected_ext: &str) -> Result<()> {
94 let kind_opt = infer::get_from_path(path)?;
95 if let Some(kind) = kind_opt {
96 let actual_ext = kind.extension();
97 if actual_ext.eq_ignore_ascii_case(expected_ext) {
98 tracing::debug!(
99 "Content type verified: {} matches expected {}",
100 actual_ext,
101 expected_ext
102 );
103 Ok(())
104 } else {
105 Err(SpsError::Generic(format!(
106 "Content type mismatch for {}: expected extension '{}', but detected '{}'",
107 path.display(),
108 expected_ext,
109 actual_ext
110 )))
111 }
112 } else {
113 Err(SpsError::Generic(format!(
114 "Could not determine content type for {}",
115 path.display()
116 )))
117 }
118}
119
120/// Validates a URL, ensuring it uses the HTTPS scheme.
121pub fn validate_url(url_str: &str) -> Result<()> {
122 let url = Url::parse(url_str)
123 .map_err(|e| SpsError::Generic(format!("Failed to parse URL '{url_str}': {e}")))?;
124 if url.scheme() == "https" {
125 Ok(())
126 } else {
127 Err(SpsError::ValidationError(format!(
128 "Invalid URL scheme for '{}': Must be https, but got '{}'",
129 url_str,
130 url.scheme()
131 )))
132 }
133}