Skip to main content

socket_patch_core/patch/
file_hash.rs

1use std::path::Path;
2
3use crate::hash::git_sha256::compute_git_sha256_from_reader;
4
5/// Compute Git-compatible SHA256 hash of file contents using streaming.
6///
7/// Gets the file size first, then streams the file through the hasher
8/// without loading the entire file into memory.
9pub async fn compute_file_git_sha256(filepath: impl AsRef<Path>) -> Result<String, std::io::Error> {
10    let filepath = filepath.as_ref();
11
12    // Get file size first
13    let metadata = tokio::fs::metadata(filepath).await?;
14    let file_size = metadata.len();
15
16    // Open file for streaming read
17    let file = tokio::fs::File::open(filepath).await?;
18    let reader = tokio::io::BufReader::new(file);
19
20    compute_git_sha256_from_reader(file_size, reader).await
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26    use crate::hash::git_sha256::compute_git_sha256_from_bytes;
27
28    #[tokio::test]
29    async fn test_compute_file_git_sha256_matches_bytes() {
30        let dir = tempfile::tempdir().unwrap();
31        let file_path = dir.path().join("test.txt");
32
33        let content = b"Hello, World!";
34        tokio::fs::write(&file_path, content).await.unwrap();
35
36        let file_hash = compute_file_git_sha256(&file_path).await.unwrap();
37        let bytes_hash = compute_git_sha256_from_bytes(content);
38
39        assert_eq!(file_hash, bytes_hash);
40    }
41
42    #[tokio::test]
43    async fn test_compute_file_git_sha256_empty_file() {
44        let dir = tempfile::tempdir().unwrap();
45        let file_path = dir.path().join("empty.txt");
46
47        tokio::fs::write(&file_path, b"").await.unwrap();
48
49        let file_hash = compute_file_git_sha256(&file_path).await.unwrap();
50        let bytes_hash = compute_git_sha256_from_bytes(b"");
51
52        assert_eq!(file_hash, bytes_hash);
53    }
54
55    #[tokio::test]
56    async fn test_compute_file_git_sha256_not_found() {
57        let result = compute_file_git_sha256("/nonexistent/file.txt").await;
58        assert!(result.is_err());
59    }
60
61    #[tokio::test]
62    async fn test_compute_file_git_sha256_large_content() {
63        let dir = tempfile::tempdir().unwrap();
64        let file_path = dir.path().join("large.bin");
65
66        // Create a file larger than the 8192 byte buffer
67        let content: Vec<u8> = (0..20000).map(|i| (i % 256) as u8).collect();
68        tokio::fs::write(&file_path, &content).await.unwrap();
69
70        let file_hash = compute_file_git_sha256(&file_path).await.unwrap();
71        let bytes_hash = compute_git_sha256_from_bytes(&content);
72
73        assert_eq!(file_hash, bytes_hash);
74    }
75}