use std::fs;
use holger_znippy_python_repository::PipRepoZnippy;
use traits::{ArtifactId, RepositoryBackendTrait};
use znippy_common::plugin::PluginRegistry;
use znippy_compress::compress_dir;
use znippy_plugin_python::NativePythonPlugin;
fn crc32(data: &[u8]) -> u32 {
let mut crc = 0xFFFF_FFFFu32;
for &b in data {
crc ^= b as u32;
for _ in 0..8 {
crc = if crc & 1 != 0 { (crc >> 1) ^ 0xEDB8_8320 } else { crc >> 1 };
}
}
!crc
}
fn build_zip_stored(name: &str, data: &[u8]) -> Vec<u8> {
let nb = name.as_bytes();
let crc = crc32(data);
let size = data.len() as u32;
let mut out = Vec::new();
let local = out.len() as u32;
out.extend_from_slice(b"PK\x03\x04");
out.extend_from_slice(&20u16.to_le_bytes());
for _ in 0..3 { out.extend_from_slice(&0u16.to_le_bytes()); }
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&crc.to_le_bytes());
out.extend_from_slice(&size.to_le_bytes());
out.extend_from_slice(&size.to_le_bytes());
out.extend_from_slice(&(nb.len() as u16).to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(nb);
out.extend_from_slice(data);
let cd = out.len() as u32;
out.extend_from_slice(b"PK\x01\x02");
out.extend_from_slice(&20u16.to_le_bytes());
out.extend_from_slice(&20u16.to_le_bytes());
for _ in 0..4 { out.extend_from_slice(&0u16.to_le_bytes()); }
out.extend_from_slice(&crc.to_le_bytes());
out.extend_from_slice(&size.to_le_bytes());
out.extend_from_slice(&size.to_le_bytes());
out.extend_from_slice(&(nb.len() as u16).to_le_bytes());
for _ in 0..3 { out.extend_from_slice(&0u16.to_le_bytes()); }
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0u32.to_le_bytes());
out.extend_from_slice(&local.to_le_bytes());
out.extend_from_slice(nb);
let cd_size = out.len() as u32 - cd;
out.extend_from_slice(b"PK\x05\x06");
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&1u16.to_le_bytes());
out.extend_from_slice(&1u16.to_le_bytes());
out.extend_from_slice(&cd_size.to_le_bytes());
out.extend_from_slice(&cd.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out
}
#[test]
fn python_backend_serves_wheel_and_sdist_via_view() {
let src = tempfile::tempdir().unwrap();
let out = tempfile::tempdir().unwrap();
let wheel = build_zip_stored("numpy-1.26.0.dist-info/METADATA", b"Name: numpy\n");
fs::create_dir_all(src.path().join("packages/numpy")).unwrap();
fs::write(
src.path().join("packages/numpy/numpy-1.26.0-cp311-cp311-manylinux_x86_64.whl"),
&wheel,
)
.unwrap();
let sdist = b"requests sdist tarball bytes".to_vec();
fs::create_dir_all(src.path().join("packages/requests")).unwrap();
fs::write(src.path().join("packages/requests/requests-2.31.0.tar.gz"), &sdist).unwrap();
let archive = out.path().join("py.znippy");
compress_dir(
&src.path().to_path_buf(),
&archive,
false,
Some(&PluginRegistry::with_plugin(Box::new(NativePythonPlugin))),
None,
None,
)
.expect("compress_dir with python plugin");
let repo = PipRepoZnippy::with_archive("pip".into(), archive).unwrap();
let wid = ArtifactId { namespace: None, name: "numpy".into(), version: "1.26.0".into() };
assert_eq!(repo.fetch(&wid).unwrap().as_deref(), Some(wheel.as_slice()));
let sid = ArtifactId { namespace: None, name: "requests".into(), version: "2.31.0".into() };
assert_eq!(repo.fetch(&sid).unwrap().as_deref(), Some(sdist.as_slice()));
let absent = ArtifactId { namespace: None, name: "numpy".into(), version: "9.9.9".into() };
assert_eq!(repo.fetch(&absent).unwrap(), None);
}