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}