diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-01-12 04:28:28 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-01-12 04:51:34 +0800 |
| commit | c5fb22694e95f12c24b8d8af76999be7aea3fcec (patch) | |
| tree | 399d8a24ce491fb635f3d09f2123290fe784059e /crates/utils/sha1_hash/src | |
| parent | 444754489aca0454eb54e15a49fb8a6db0b68a07 (diff) | |
Reorganize crate structure and move documentation files
Diffstat (limited to 'crates/utils/sha1_hash/src')
| -rw-r--r-- | crates/utils/sha1_hash/src/lib.rs | 257 |
1 files changed, 0 insertions, 257 deletions
diff --git a/crates/utils/sha1_hash/src/lib.rs b/crates/utils/sha1_hash/src/lib.rs deleted file mode 100644 index 96a7897..0000000 --- a/crates/utils/sha1_hash/src/lib.rs +++ /dev/null @@ -1,257 +0,0 @@ -use sha1::{Digest, Sha1}; -use std::path::{Path, PathBuf}; -use std::sync::Arc; -use tokio::fs::File; -use tokio::io::{AsyncReadExt, BufReader}; -use tokio::task; - -/// # Struct - Sha1Result -/// -/// Records SHA1 calculation results, including the file path and hash value -#[derive(Debug, Clone)] -pub struct Sha1Result { - pub file_path: PathBuf, - pub hash: String, -} - -/// Calc SHA1 hash of a string -pub fn calc_sha1_string<S: AsRef<str>>(input: S) -> String { - let mut hasher = Sha1::new(); - hasher.update(input.as_ref().as_bytes()); - let hash_result = hasher.finalize(); - - hash_result - .iter() - .map(|b| format!("{:02x}", b)) - .collect::<String>() -} - -/// Calc SHA1 hash of a single file -pub async fn calc_sha1<P: AsRef<Path>>( - path: P, - buffer_size: usize, -) -> Result<Sha1Result, Box<dyn std::error::Error + Send + Sync>> { - let file_path = path.as_ref().to_string_lossy().to_string(); - - // Open file asynchronously - let file = File::open(&path).await?; - let mut reader = BufReader::with_capacity(buffer_size, file); - let mut hasher = Sha1::new(); - let mut buffer = vec![0u8; buffer_size]; - - // Read file in chunks and update hash asynchronously - loop { - let n = reader.read(&mut buffer).await?; - if n == 0 { - break; - } - hasher.update(&buffer[..n]); - } - - let hash_result = hasher.finalize(); - - // Convert to hex string - let hash_hex = hash_result - .iter() - .map(|b| format!("{:02x}", b)) - .collect::<String>(); - - Ok(Sha1Result { - file_path: file_path.into(), - hash: hash_hex, - }) -} - -/// Calc SHA1 hashes for multiple files using multi-threading -pub async fn calc_sha1_multi<P, I>( - paths: I, - buffer_size: usize, -) -> Result<Vec<Sha1Result>, Box<dyn std::error::Error + Send + Sync>> -where - P: AsRef<Path> + Send + Sync + 'static, - I: IntoIterator<Item = P>, -{ - let buffer_size = Arc::new(buffer_size); - - // Collect all file paths - let file_paths: Vec<P> = paths.into_iter().collect(); - - if file_paths.is_empty() { - return Ok(Vec::new()); - } - - // Create tasks for each file - let tasks: Vec<_> = file_paths - .into_iter() - .map(|path| { - let buffer_size = Arc::clone(&buffer_size); - task::spawn(async move { calc_sha1(path, *buffer_size).await }) - }) - .collect(); - - // Execute tasks with concurrency limit using join_all - let results: Vec<Result<Sha1Result, Box<dyn std::error::Error + Send + Sync>>> = - futures::future::join_all(tasks) - .await - .into_iter() - .map(|task_result| match task_result { - Ok(Ok(calc_result)) => Ok(calc_result), - Ok(Err(e)) => Err(e), - Err(e) => Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>), - }) - .collect(); - - // Check for any errors and collect successful results - let mut successful_results = Vec::new(); - for result in results { - match result { - Ok(success) => successful_results.push(success), - Err(e) => return Err(e), - } - } - - Ok(successful_results) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs; - - #[test] - fn test_sha1_string() { - let test_string = "Hello, SHA1!"; - let hash = calc_sha1_string(test_string); - - let expected_hash = "de1c3daadc6f0f1626f4cf56c03e05a1e5d7b187"; - - assert_eq!( - hash, expected_hash, - "SHA1 hash should be consistent for same input" - ); - } - - #[test] - fn test_sha1_string_empty() { - let hash = calc_sha1_string(""); - - // SHA1 of empty string is "da39a3ee5e6b4b0d3255bfef95601890afd80709" - let expected_empty_hash = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; - assert_eq!( - hash, expected_empty_hash, - "SHA1 hash mismatch for empty string" - ); - } - - #[tokio::test] - async fn test_sha1_accuracy() { - // Test file path relative to the crate root - let test_file_path = "res/story.txt"; - // Choose expected hash file based on platform - let expected_hash_path = if cfg!(windows) { - "res/story_crlf.sha1" - } else { - "res/story_lf.sha1" - }; - - // Calculate SHA1 hash - let result = calc_sha1(test_file_path, 8192) - .await - .expect("Failed to calculate SHA1"); - - // Read expected hash from file - let expected_hash = fs::read_to_string(expected_hash_path) - .expect("Failed to read expected hash file") - .trim() - .to_string(); - - // Verify the calculated hash matches expected hash - assert_eq!( - result.hash, expected_hash, - "SHA1 hash mismatch for test file" - ); - - println!("Test file: {}", result.file_path.display()); - println!("Calculated hash: {}", result.hash); - println!("Expected hash: {}", expected_hash); - println!( - "Platform: {}", - if cfg!(windows) { - "Windows" - } else { - "Unix/Linux" - } - ); - } - - #[tokio::test] - async fn test_sha1_empty_file() { - // Create a temporary empty file for testing - let temp_file = "test_empty.txt"; - fs::write(temp_file, "").expect("Failed to create empty test file"); - - let result = calc_sha1(temp_file, 4096) - .await - .expect("Failed to calculate SHA1 for empty file"); - - // SHA1 of empty string is "da39a3ee5e6b4b0d3255bfef95601890afd80709" - let expected_empty_hash = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; - assert_eq!( - result.hash, expected_empty_hash, - "SHA1 hash mismatch for empty file" - ); - - // Clean up - fs::remove_file(temp_file).expect("Failed to remove temporary test file"); - } - - #[tokio::test] - async fn test_sha1_simple_text() { - // Create a temporary file with simple text - let temp_file = "test_simple.txt"; - let test_content = "Hello, SHA1!"; - fs::write(temp_file, test_content).expect("Failed to create simple test file"); - - let result = calc_sha1(temp_file, 4096) - .await - .expect("Failed to calculate SHA1 for simple text"); - - // Note: This test just verifies that the function works without errors - // The actual hash value is not critical for this test - - println!("Simple text test - Calculated hash: {}", result.hash); - - // Clean up - fs::remove_file(temp_file).expect("Failed to remove temporary test file"); - } - - #[tokio::test] - async fn test_sha1_multi_files() { - // Test multiple files calculation - let test_files = vec!["res/story.txt"]; - - let results = calc_sha1_multi(test_files, 8192) - .await - .expect("Failed to calculate SHA1 for multiple files"); - - assert_eq!(results.len(), 1, "Should have calculated hash for 1 file"); - - // Choose expected hash file based on platform - let expected_hash_path = if cfg!(windows) { - "res/story_crlf.sha1" - } else { - "res/story_lf.sha1" - }; - - // Read expected hash from file - let expected_hash = fs::read_to_string(expected_hash_path) - .expect("Failed to read expected hash file") - .trim() - .to_string(); - - assert_eq!( - results[0].hash, expected_hash, - "SHA1 hash mismatch in multi-file test" - ); - } -} |
