1use std::collections::HashMap;
2use std::num::NonZeroU64;
3
4#[repr(transparent)]
5#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
6pub struct Inode(NonZeroU64);
7
8impl Inode {
9 pub fn new(value: u64) -> std::io::Result<Inode> {
10 match NonZeroU64::new(value) {
11 Some(v) => Ok(Inode(v)),
12 None => Err(std::io::Error::new(
13 std::io::ErrorKind::InvalidInput,
14 "inode must not be zero",
15 )),
16 }
17 }
18
19 pub fn get(self) -> u64 {
20 self.0.get()
21 }
22}
23
24pub(crate) mod meta;
25#[cfg(feature = "reader")]
26pub mod reader;
27#[cfg(feature = "writer")]
28pub mod writer;
29
30pub use self::meta::BoxMetadata;
31
32pub type AttrMap = HashMap<usize, Vec<u8>>;
33
34#[cfg(test)]
35mod tests {
36 use crate::{compression::Compression, *};
37 use std::collections::HashMap;
38 use std::io::prelude::*;
39 use std::io::Cursor;
40 use std::path::Path;
41
42 fn create_test_box<F: AsRef<Path>>(filename: F) {
43 let _ = std::fs::remove_file(filename.as_ref());
44
45 let mut cursor: Cursor<Vec<u8>> = Cursor::new(vec![]);
46 let data = b"hello\0\0\0";
47 cursor.write_all(data).unwrap();
48 cursor.seek(std::io::SeekFrom::Start(0)).unwrap();
49
50 let mut writer = BoxFileWriter::create(filename).unwrap();
51 writer
52 .insert(
53 Compression::Stored,
54 BoxPath::new("hello.txt").unwrap(),
55 &mut cursor,
56 HashMap::new(),
57 )
58 .unwrap();
59 }
60
61 #[test]
62 fn create_box_file() {
63 create_test_box("./smoketest.box");
64 }
65
66 #[test]
67 fn read_garbage() {
68 let filename = "./read_garbage.box";
69 create_test_box(&filename);
70
71 let bf = BoxFileReader::open(&filename).unwrap();
72 let trailer = bf.metadata();
73 println!("{:?}", bf.header);
74 println!("{:?}", &trailer);
75 let file_data = unsafe {
76 bf.memory_map(&trailer.inodes[0].as_file().unwrap())
77 .unwrap()
78 };
79 println!("{:?}", &*file_data);
80 assert_eq!(&*file_data, b"hello\0\0\0")
81 }
82
83 #[test]
84 fn create_garbage() {
85 let filename = "./create_garbage.box";
86 let _ = std::fs::remove_file(&filename);
87 let bf = BoxFileWriter::create(&filename).expect("Mah box");
88 bf.finish().unwrap();
89 }
90
91 #[test]
92 fn read_bytes() {
93 let filename = "./read_bytes.box";
94 create_test_box(&filename);
95 let bf = BoxFileReader::open(&filename).unwrap();
96 let record = bf
97 .metadata()
98 .inodes
99 .first()
100 .map(|f| f.as_file().unwrap())
101 .unwrap();
102 let mut reader = bf.read_bytes(&record).unwrap();
103 let mut vec = vec![];
104 reader.read_to_end(&mut vec).unwrap();
105 assert_eq!(vec, b"hello\0\0\0")
106 }
107
108 fn insert_impl<F>(filename: &str, f: F)
109 where
110 F: Fn(&str) -> BoxFileWriter,
111 {
112 let _ = std::fs::remove_file(&filename);
113 let v =
114 "This, this, this, this, this is a compressable string string string string string.\n"
115 .to_string();
116
117 {
118 use std::time::SystemTime;
119 let now = SystemTime::now()
120 .duration_since(SystemTime::UNIX_EPOCH)
121 .unwrap()
122 .as_secs()
123 .to_le_bytes();
124
125 let mut bf = f(filename);
126
127 let mut dir_attrs = HashMap::new();
128 dir_attrs.insert("created".into(), now.to_vec());
129 dir_attrs.insert("unix.mode".into(), 0o755u16.to_le_bytes().to_vec());
130
131 let mut attrs = HashMap::new();
132 attrs.insert("created".into(), now.to_vec());
133 attrs.insert("unix.mode".into(), 0o644u16.to_le_bytes().to_vec());
134
135 bf.mkdir(BoxPath::new("test").unwrap(), dir_attrs).unwrap();
136
137 bf.insert(
138 Compression::Zstd,
139 BoxPath::new("test/string.txt").unwrap(),
140 &mut std::io::Cursor::new(v.clone()),
141 attrs.clone(),
142 )
143 .unwrap();
144 bf.insert(
145 Compression::Deflate,
146 BoxPath::new("test/string2.txt").unwrap(),
147 &mut std::io::Cursor::new(v.clone()),
148 attrs.clone(),
149 )
150 .unwrap();
151 bf.finish().unwrap();
153 }
154
155 let bf = BoxFileReader::open(&filename).expect("Mah box");
156 println!("{:#?}", &bf);
157
158 assert_eq!(
159 v,
160 bf.decompress_value::<String>(&bf.meta.inodes[1].as_file().unwrap())
161 .unwrap()
162 );
163 assert_eq!(
164 v,
165 bf.decompress_value::<String>(&bf.meta.inodes[2].as_file().unwrap())
166 .unwrap()
167 );
168 }
169
170 #[test]
171 fn insert() {
172 insert_impl("./insert_garbage.box", |n| {
173 BoxFileWriter::create(n).unwrap()
174 });
175 insert_impl("./insert_garbage_align8.box", |n| {
176 BoxFileWriter::create_with_alignment(n, 8).unwrap()
177 });
178 insert_impl("./insert_garbage_align7.box", |n| {
179 BoxFileWriter::create_with_alignment(n, 7).unwrap()
180 });
181 }
182
183 }