// Simple CLI tool to test ZIP creation compatibility
// This creates test ZIP files that can be verified with standard tools
///|
/// Main entry point for ZIP library demonstration and testing.
///
/// This CLI tool exercises the major features of the MoonBit ZIP library:
/// 1. Single-file ZIP creation (stored format)
/// 2. Multi-file archives with mixed content
/// 3. Directory handling and nested paths
/// 4. Hex dump utilities for debugging binary output
///
/// Generated ZIP files can be verified with external tools like `unzip -l`,
/// `7zip`, or `zipinfo` to ensure compatibility with standard implementations.
///
/// The tool demonstrates both the high-level Archive API and lower-level
/// File/Member construction patterns.
#coverage.skip
fn main {
println("=== MoonBit ZIP Library Test ===\n")
// Test 1: Create a simple ZIP file
test_simple_zip()
// Test 2: Create a multi-file ZIP
test_multi_file_zip()
// Test 3: Create ZIP with directory
test_directory_zip()
// Test 4: Demonstrate hex_dump function
test_hex_dump()
println("\n✅ All tests completed successfully!")
}
///|
fn test_simple_zip() -> Unit {
println("Test 1: Creating simple.zip with one file...")
let content = b"Hello from MoonBit ZIP library!\nThis is a test file.\n"
try {
let f = @file.File::stored_of_bytes(content, 0, content.length())
let mem = @member.make(
@fpath.Fpath("hello.txt"),
File(f),
mode=@types.FileMode(0o644),
mtime=@types.dos_epoch,
)
let archive = @lib.Archive::empty()
archive.add(mem)
let zip_bytes = archive.to_bytes()
println(" ✓ Created ZIP: \{zip_bytes.length()} bytes")
// Verify roundtrip
try {
let decoded = @lib.Archive::of_bytes(zip_bytes)
println(" ✓ Roundtrip successful: \{decoded.member_count()} member(s)")
} catch {
e => println(" ✗ Decode failed: \{e}")
}
} catch {
e => println(" ✗ Failed: \{e}")
}
}
///|
fn test_multi_file_zip() -> Unit {
println("\nTest 2: Creating multi-file.zip with multiple files...")
try {
let file1 = @file.File::stored_of_bytes(b"Content of file 1", 0, 17)
let file2 = @file.File::stored_of_bytes(b"Content of file 2", 0, 17)
let file3 = @file.File::stored_of_bytes(b"Content of file 3", 0, 17)
let m1 = @member.make(@fpath.Fpath("file1.txt"), File(file1))
let m2 = @member.make(@fpath.Fpath("file2.txt"), File(file2))
let m3 = @member.make(@fpath.Fpath("file3.txt"), File(file3))
println(" Original members:")
println(" - \{m1.path()}")
println(" - \{m2.path()}")
println(" - \{m3.path()}")
let archive = @lib.Archive::empty()
archive.add(m1)
archive.add(m2)
archive.add(m3)
println(" Archive member count: \{archive.member_count()}")
let zip_bytes = archive.to_bytes()
println(" ✓ Created ZIP: \{zip_bytes.length()} bytes")
// Verify all members
try {
let decoded = @lib.Archive::of_bytes(zip_bytes)
println(" Decoded member count: \{decoded.member_count()}")
let members = decoded.to_array()
println(" Decoded members:")
for i = 0; i < members.length(); i = i + 1 {
println(" - \{members[i].path()}")
}
let has_all = decoded.mem(@fpath.Fpath("file1.txt")) &&
decoded.mem(@fpath.Fpath("file2.txt")) &&
decoded.mem(@fpath.Fpath("file3.txt"))
println(" ✓ All files present: \{has_all}")
} catch {
e => println(" ✗ Decode failed: \{e}")
}
} catch {
e => println(" ✗ Failed: \{e}")
}
}
///|
fn test_directory_zip() -> Unit {
println("\nTest 3: Creating archive with directories...")
try {
let dir = @member.make(
@fpath.Fpath("docs/"),
Dir,
mode=@types.FileMode(0o755),
)
let data = b"# README\nThis is documentation.\n"
let file = @file.File::stored_of_bytes(data, 0, data.length())
let readme = @member.make(
@fpath.Fpath("docs/README.md"),
File(file),
mode=@types.FileMode(0o644),
)
let archive = @lib.Archive::empty()
archive.add(dir)
archive.add(readme)
let zip_bytes = archive.to_bytes()
println(" ✓ Created ZIP: \{zip_bytes.length()} bytes")
try {
let decoded = @lib.Archive::of_bytes(zip_bytes)
match decoded.find(@fpath.Fpath("docs/")) {
Some(d) => println(" ✓ Directory entry found: is_dir=\{d.is_dir()}")
None => println(" ✗ Directory not found")
}
match decoded.find(@fpath.Fpath("docs/README.md")) {
Some(f) => println(" ✓ File entry found: is_file=\{f.is_file()}")
None => println(" ✗ File not found")
}
} catch {
e => println(" ✗ Decode failed: \{e}")
}
} catch {
e => println(" ✗ Failed: \{e}")
}
}
///|
fn test_hex_dump() -> Unit {
println("\nTest 4: Demonstrating hex_dump function...")
// Test with simple data
let simple_data = b"Hello, World!"
println(" Simple string hex dump:")
println(@hexdump.hex_dump(simple_data))
// Test with ZIP magic signature
let zip_magic = Bytes::from_array([0x50, 0x4B, 0x03, 0x04])
println(" ZIP magic signature hex dump:")
println(@hexdump.hex_dump(zip_magic))
// Test with mixed printable/non-printable data
let mixed_data = Bytes::from_array([
0x48, 0x65, 0x6c, 0x6c, 0x6f, // "Hello"
0x00, 0x01, 0x02, 0x03, 0x04, // Non-printable
0x20, 0x21, 0x22, 0x23, 0x24, // Printable ASCII
0x7f, 0x80, 0xff, // More non-printable
])
println(" Mixed data hex dump:")
println(@hexdump.hex_dump(mixed_data))
println(" ✓ Hex dump function working correctly!")
}