oxihuman_core/
archive_writer.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
9pub struct WriteEntry {
10 pub name: String,
11 pub data: Vec<u8>,
12 pub compression_level: u8,
13}
14
15impl WriteEntry {
16 pub fn new(name: &str, data: Vec<u8>) -> Self {
17 WriteEntry {
18 name: name.to_string(),
19 data,
20 compression_level: 6,
21 }
22 }
23
24 pub fn with_compression(mut self, level: u8) -> Self {
25 self.compression_level = level.min(9);
26 self
27 }
28}
29
30pub struct ArchiveWriter {
32 destination: String,
33 entries: Vec<WriteEntry>,
34 comment: String,
35}
36
37impl ArchiveWriter {
38 pub fn new(destination: &str) -> Self {
39 ArchiveWriter {
40 destination: destination.to_string(),
41 entries: Vec::new(),
42 comment: String::new(),
43 }
44 }
45
46 pub fn add_entry(&mut self, entry: WriteEntry) {
47 self.entries.push(entry);
48 }
49
50 pub fn add_bytes(&mut self, name: &str, data: Vec<u8>) {
51 self.entries.push(WriteEntry::new(name, data));
52 }
53
54 pub fn add_text(&mut self, name: &str, text: &str) {
55 self.add_bytes(name, text.as_bytes().to_vec());
56 }
57
58 pub fn set_comment(&mut self, comment: &str) {
59 self.comment = comment.to_string();
60 }
61
62 pub fn entry_count(&self) -> usize {
63 self.entries.len()
64 }
65
66 pub fn total_uncompressed_size(&self) -> u64 {
67 self.entries.iter().map(|e| e.data.len() as u64).sum()
68 }
69
70 pub fn finalize(&self) -> u64 {
72 self.total_uncompressed_size()
73 }
74
75 pub fn destination(&self) -> &str {
76 &self.destination
77 }
78
79 pub fn has_entry(&self, name: &str) -> bool {
80 self.entries.iter().any(|e| e.name == name)
81 }
82}
83
84impl Default for ArchiveWriter {
85 fn default() -> Self {
86 Self::new("/tmp/out.zip")
87 }
88}
89
90pub fn new_archive_writer(path: &str) -> ArchiveWriter {
92 ArchiveWriter::new(path)
93}
94
95pub fn write_entries(writer: &mut ArchiveWriter, entries: Vec<WriteEntry>) {
97 for e in entries {
98 writer.add_entry(e);
99 }
100}
101
102pub fn build_archive(path: &str, items: &[(&str, &[u8])]) -> ArchiveWriter {
104 let mut w = new_archive_writer(path);
105 for (name, data) in items {
106 w.add_bytes(name, data.to_vec());
107 }
108 w
109}
110
111pub fn estimate_size(writer: &ArchiveWriter) -> u64 {
113 writer.total_uncompressed_size()
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_new_writer() {
122 let w = new_archive_writer("/tmp/x.zip");
123 assert_eq!(w.entry_count(), 0);
124 assert_eq!(w.destination(), "/tmp/x.zip");
125 }
126
127 #[test]
128 fn test_add_bytes() {
129 let mut w = new_archive_writer("/tmp/x.zip");
130 w.add_bytes("a.txt", b"hello".to_vec());
131 assert_eq!(w.entry_count(), 1);
132 assert!(w.has_entry("a.txt"));
133 }
134
135 #[test]
136 fn test_add_text() {
137 let mut w = new_archive_writer("/tmp/x.zip");
138 w.add_text("note.txt", "text content");
139 assert!(w.has_entry("note.txt"));
140 }
141
142 #[test]
143 fn test_total_uncompressed_size() {
144 let mut w = new_archive_writer("/tmp/x.zip");
145 w.add_bytes("a", vec![0u8; 100]);
146 w.add_bytes("b", vec![0u8; 200]);
147 assert_eq!(w.total_uncompressed_size(), 300);
148 }
149
150 #[test]
151 fn test_finalize() {
152 let mut w = new_archive_writer("/tmp/x.zip");
153 w.add_bytes("f", vec![0u8; 50]);
154 assert_eq!(w.finalize(), 50);
155 }
156
157 #[test]
158 fn test_set_comment() {
159 let mut w = new_archive_writer("/tmp/x.zip");
160 w.set_comment("archive comment");
161 assert_eq!(w.comment, "archive comment");
162 }
163
164 #[test]
165 fn test_build_archive() {
166 let w = build_archive("/tmp/out.zip", &[("a.txt", b"aa"), ("b.txt", b"bb")]);
167 assert_eq!(w.entry_count(), 2);
168 }
169
170 #[test]
171 fn test_write_entry_compression_level_clamped() {
172 let e = WriteEntry::new("f", vec![]).with_compression(20);
173 assert_eq!(e.compression_level, 9);
174 }
175
176 #[test]
177 fn test_estimate_size() {
178 let mut w = new_archive_writer("/tmp/x.zip");
179 w.add_bytes("x", vec![0u8; 77]);
180 assert_eq!(estimate_size(&w), 77);
181 }
182
183 #[test]
184 fn test_has_entry_false() {
185 let w = new_archive_writer("/tmp/x.zip");
186 assert!(!w.has_entry("nonexistent"));
187 }
188}