1#![doc = include_str!("../README.md")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/MrVintage710/pak/refs/heads/main/docs/icon.png")]
3
4use std::{cell::RefCell, collections::HashMap, fmt::Debug, fs::{self, File}, io::{BufReader, Cursor, Read, Seek, SeekFrom}, path::Path};
5use btree::{PakTree, PakTreeBuilder};
6use index::PakIndex;
7use item::{PakItemDeserialize, PakItemDeserializeGroup, PakItemSearchable, PakItemSerialize};
8use meta::{PakMeta, PakSizing};
9use pointer::{PakPointer, PakUntypedPointer};
10use query::PakQueryExpression;
11
12use crate::error::PakResult;
13
14#[cfg(test)]
15mod test;
16
17pub mod meta;
18pub mod item;
19pub mod index;
20pub mod value;
21pub(crate) mod btree;
22pub mod query;
23pub mod error;
24pub mod pointer;
25
26pub struct Pak {
32 sizing : PakSizing,
33 meta : PakMeta,
34 source : RefCell<Box<dyn PakSource>>
35}
36
37impl Pak {
38 pub fn new<S>(mut source : S) -> PakResult<Self> where S : PakSource + 'static {
40 let sizing_pointer = PakPointer::new_untyped(0, 24);
41 let sizing_buffer = source.read(&sizing_pointer, 0)?;
42 let sizing : PakSizing = bincode::deserialize(&sizing_buffer)?;
43
44 let meta_pointer = PakPointer::new_untyped(24, sizing.meta_size);
45 let meta_buffer = source.read(&meta_pointer, 0)?;
46 let meta : PakMeta = bincode::deserialize(&meta_buffer)?;
47
48 Ok(Self { sizing, source : RefCell::new(Box::new(source)), meta })
49 }
50
51 pub fn new_from_file<P>(path : P) -> PakResult<Self> where P : AsRef<Path> {
53 let file = File::open(path)?;
54 Self::new(BufReader::new(file))
55 }
56
57 pub fn query<T>(&self, query : impl PakQueryExpression) -> PakResult<T::ReturnType> where T : PakItemDeserializeGroup {
59 let pointers = query.execute(self)?.into_iter().collect();
60 T::deserialize_group(self, pointers)
61 }
62
63 pub fn query_sql<T>(&self, pql : &str) -> PakResult<T::ReturnType> where T : PakItemDeserializeGroup {
65 let query = crate::query::pql::pql(pql)?;
66 self.query::<T>(query)
67 }
68
69 pub fn size(&self) -> u64 {
71 24 + self.sizing.meta_size + self.sizing.indices_size + self.sizing.vault_size
72 }
73
74 pub fn name(&self) -> &str {
76 &self.meta.name
77 }
78
79 pub fn version(&self) -> &str {
81 &self.meta.version
82 }
83
84 pub fn author(&self) -> &str {
86 &self.meta.author
87 }
88
89 pub fn description(&self) -> &str {
91 &self.meta.description
92 }
93
94 pub fn read_err<T>(&self, pointer : &PakPointer) -> PakResult<T> where T : PakItemDeserialize {
95 if !pointer.type_is_match::<T>() { return Err(error::PakError::TypeMismatchError(pointer.type_name().to_string(), std::any::type_name::<T>().to_string())) }
96 let buffer = self.source.borrow_mut().read(pointer, self.get_vault_start())?;
97 let res = T::from_bytes(&buffer)?;
98 Ok(res)
99 }
100
101 pub fn read<T>(&self, pointer : &PakPointer) -> Option<T> where T : PakItemDeserialize {
102 let res = self.read_err::<T>(pointer);
103 match res {
104 Ok(res) => Some(res),
105 Err(_) => None,
106 }
107 }
108
109 pub(crate) fn get_tree(&self, key : &str) -> PakResult<PakTree<'_>> {
110 PakTree::new(self, key)
111 }
112
113 pub(crate) fn fetch_indices(&self) -> PakResult<HashMap<String, PakUntypedPointer>> {
114 let pointer = PakPointer::new_untyped(self.get_indices_start(), self.sizing.indices_size);
115 let buffer = self.source.borrow_mut().read(&pointer, 0)?;
116 let indices = bincode::deserialize(&buffer)?;
117 Ok(indices)
118 }
119
120 pub(crate) fn get_vault_start(&self) -> u64 {
121 24 + self.sizing.meta_size + self.sizing.indices_size + 8
123 }
124
125 pub(crate) fn get_indices_start(&self) -> u64 {
126 24 + self.sizing.meta_size
127 }
128
129}
130
131pub trait PakSource {
137 fn read(&mut self, pointer : &PakPointer, offset : u64) -> PakResult<Vec<u8>>;
139}
140
141impl <R> PakSource for R where R : Read + Seek {
142 fn read(&mut self, pointer : &PakPointer, offset : u64) -> PakResult<Vec<u8>> {
143 let mut buffer = vec![0u8; pointer.size() as usize];
144 self.seek(SeekFrom::Start(pointer.offset() + offset))?;
145 self.read_exact(&mut buffer)?;
146 Ok(buffer)
147 }
148}
149
150pub struct PakBuilder {
156 chunks : Vec<PakVaultReference>,
157 size_in_bytes : u64,
158 vault : Vec<u8>,
159 name: String,
160 description: String,
161 author: String,
162}
163
164impl PakBuilder {
165 pub fn new() -> Self {
167 Self {
168 vault : Vec::new(),
169 chunks : Vec::new(),
170 size_in_bytes : 0,
171 name: String::new(),
172 description: String::new(),
173 author: String::new(),
174 }
175 }
176
177 pub fn pak_no_search<T: PakItemSerialize>(&mut self, item : T) -> PakResult<PakPointer> {
179 let bytes = item.into_bytes()?;
180 let pointer = PakPointer::new_typed::<T>(self.size_in_bytes, bytes.len() as u64);
181 self.size_in_bytes += bytes.len() as u64;
182 self.vault.extend(bytes);
183 self.chunks.push(PakVaultReference { pointer: pointer.clone(), indices: vec![] });
184 Ok(pointer)
185 }
186
187 pub fn pak<T : PakItemSerialize + PakItemSearchable>(&mut self, item : T) -> PakResult<PakPointer> {
189 let indices = item.get_indices();
190 let bytes = item.into_bytes()?;
191 let pointer = PakPointer::new_typed::<T>(self.size_in_bytes, bytes.len() as u64);
192 self.size_in_bytes += bytes.len() as u64;
193 self.vault.extend(bytes);
194 self.chunks.push(PakVaultReference { pointer: pointer.clone(), indices: indices.clone() });
195 Ok(pointer)
196 }
197
198 pub fn size(&self) -> u64 {
200 self.size_in_bytes
201 }
202
203 pub fn len(&self) -> usize {
205 self.chunks.len()
206 }
207
208 pub fn with_name(mut self, name: &str) -> Self {
210 self.name = name.to_string();
211 self
212 }
213
214 pub fn with_description(mut self, description: &str) -> Self {
216 self.description = description.to_string();
217 self
218 }
219
220 pub fn with_author(mut self, author: &str) -> Self {
222 self.author = author.to_string();
223 self
224 }
225
226 pub fn set_name(&mut self, name: &str) {
228 self.name = name.to_string();
229 }
230
231 pub fn set_description(&mut self, description: &str) {
233 self.description = description.to_string();
234 }
235
236 pub fn set_author(&mut self, author: &str) {
238 self.author = author.to_string();
239 }
240
241 pub fn build_file(self, path : impl AsRef<Path>) -> PakResult<Pak> {
243 let (out, sizing, meta) = self.build_internal()?;
244
245 fs::write(&path, out)?;
246 let pak = Pak {
247 sizing,
248 meta,
249 source: RefCell::new(Box::new(BufReader::new(File::open(path)?))),
250 };
251 Ok(pak)
252 }
253
254 pub fn build_in_memory(self) -> PakResult<Pak> {
256 let (out, sizing, meta) = self.build_internal()?;
257
258 let pak = Pak {
259 sizing,
260 meta,
261 source: RefCell::new(Box::new(Cursor::new(out))),
262 };
263 Ok(pak)
264 }
265
266 fn build_internal(mut self) -> PakResult<(Vec<u8>, PakSizing, PakMeta)> {
267 let mut map : HashMap<String, PakTreeBuilder> = HashMap::new();
268 for chunk in &self.chunks {
269 for index in &chunk.indices{
270 map.entry(index.key.clone())
271 .or_insert(PakTreeBuilder::new(6))
272 .access()
273 .insert(index.value.clone(), chunk.pointer.clone())
274 ;
275 }
276 }
277
278 let mut pointer_map : HashMap<String, PakUntypedPointer> = HashMap::new();
279 for (key, tree) in map {
280 let pointer = tree.into_pak(&mut self)?;
281 pointer_map.insert(key, pointer.as_untyped());
282 }
283
284 let meta = PakMeta {
285 name: self.name,
286 description: self.description,
287 author: self.author,
288 version: "1.0".to_string(),
289 };
290
291 let sizing = PakSizing {
292 meta_size: bincode::serialized_size(&meta)?,
293 indices_size: bincode::serialized_size(&pointer_map)?,
294 vault_size: bincode::serialized_size(&self.vault)?,
295 };
296
297 let mut sizing_out = bincode::serialize(&sizing)?;
298 let mut meta_out = bincode::serialize(&meta)?;
299 let mut pointer_map_out = bincode::serialize(&pointer_map)?;
300 let mut vault_out = bincode::serialize(&self.vault)?;
301
302 let mut out = Vec::<u8>::new();
303 out.append(&mut sizing_out);
304 out.append(&mut meta_out);
305 out.append(&mut pointer_map_out);
306 out.append(&mut vault_out);
307 Ok((out, sizing, meta))
308 }
309
310}
311
312#[derive(Debug, Clone)]
317pub(crate) struct PakVaultReference {
318 pointer : PakPointer,
319 indices : Vec<PakIndex>
320}