git_internal/
lib.rs

1//! Git-Internal is a library for encoding and decoding Git Pack format files or streams.
2
3pub mod errors;
4pub mod hash;
5pub mod internal;
6pub mod utils;
7mod zstdelta;
8mod delta;
9
10// #[cfg(test)]
11pub mod test_utils {
12    use reqwest::Client;
13    use ring::digest::{Context, SHA256};
14    use std::collections::HashMap;
15    use std::env;
16    use std::fs::File;
17    use std::io::copy;
18    use std::path::PathBuf;
19    use tokio::io::{AsyncReadExt, AsyncSeekExt};
20    use tokio::sync::OnceCell;
21
22    static FILES_READY: OnceCell<HashMap<String, PathBuf>> = OnceCell::const_new();
23
24    pub async fn setup_lfs_file() -> HashMap<String, PathBuf> {
25        FILES_READY
26            .get_or_init(|| async {
27                let files_to_download = vec![(
28                    "git-2d187177923cd618a75da6c6db45bb89d92bd504.pack",
29                    "0d1f01ac02481427e83ba6c110c7a3a75edd4264c5af8014d12d6800699c8409",
30                )];
31
32                let mut map = HashMap::new();
33                let mut base_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
34                base_dir.pop();
35                base_dir.push("tests/data/packs");
36                for (name, sha256) in files_to_download {
37                    let file_path = base_dir.join(name);
38                    download_lfs_file_if_not_exist(name, &file_path, sha256).await;
39
40                    map.insert(name.to_string(), file_path);
41                }
42
43                map
44            })
45            .await
46            .clone()
47    }
48
49    async fn calculate_checksum(file: &mut tokio::fs::File, checksum: &mut Context) {
50        file.seek(tokio::io::SeekFrom::Start(0)).await.unwrap();
51        let mut buf = [0u8; 8192];
52        loop {
53            let n = file.read(&mut buf).await.unwrap();
54            if n == 0 {
55                break;
56            }
57            checksum.update(&buf[..n]);
58        }
59    }
60
61    async fn download_file(
62        url: &str,
63        output_path: &PathBuf,
64    ) -> Result<(), Box<dyn std::error::Error>> {
65        let response = Client::new().get(url).send().await?;
66        let mut file = File::create(output_path)?;
67        let content = response.bytes().await?;
68        let mut content = content.as_ref();
69        copy(&mut content, &mut file)?;
70        Ok(())
71    }
72
73    async fn check_file_hash(output_path: &PathBuf, sha256: &str) -> bool {
74        if output_path.exists() {
75            let mut ring_context = Context::new(&SHA256);
76            let mut file = tokio::fs::File::open(output_path).await.unwrap();
77            calculate_checksum(&mut file, &mut ring_context).await;
78            let checksum = hex::encode(ring_context.finish().as_ref());
79            checksum == sha256
80        } else {
81            false
82        }
83    }
84
85    async fn download_lfs_file_if_not_exist(file_name: &str, output_path: &PathBuf, sha256: &str) {
86        let url = format!(
87            "https://file.gitmega.com/packs/{file_name}",
88            // "https://gitmono.s3.ap-southeast-2.amazonaws.com/packs/{file_name}",
89            // "https://gitmono.oss-cn-beijing.aliyuncs.com/{}",
90        );
91        if !check_file_hash(output_path, sha256).await {
92            download_file(&url, output_path).await.unwrap();
93        }
94    }
95}