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, PakTypedPointer, 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().map(|i| i.into_pointer()).collect();
60 T::deserialize_group(self, pointers)
61 }
62
63 pub fn size(&self) -> u64 {
65 24 + self.sizing.meta_size + self.sizing.indices_size + self.sizing.vault_size
66 }
67
68 pub fn name(&self) -> &str {
70 &self.meta.name
71 }
72
73 pub fn version(&self) -> &str {
75 &self.meta.version
76 }
77
78 pub fn author(&self) -> &str {
80 &self.meta.author
81 }
82
83 pub fn description(&self) -> &str {
85 &self.meta.description
86 }
87
88 pub(crate) fn read_err<T>(&self, pointer : &PakPointer) -> PakResult<T> where T : PakItemDeserialize {
89 if !pointer.type_is_match::<T>() { return Err(error::PakError::TypeMismatchError(pointer.type_name().to_string(), std::any::type_name::<T>().to_string())) }
90 let buffer = self.source.borrow_mut().read(pointer, self.get_vault_start())?;
91 let res = T::from_bytes(&buffer)?;
92 Ok(res)
93 }
94
95 pub(crate) fn read<T>(&self, pointer : &PakPointer) -> Option<T> where T : PakItemDeserialize {
96 let res = self.read_err(pointer);
97 match res {
98 Ok(res) => Some(res),
99 Err(_) => None,
100 }
101 }
102
103 pub(crate) fn get_tree(&self, key : &str) -> PakResult<PakTree> {
104 PakTree::new(self, key)
105 }
106
107 pub(crate) fn fetch_indices(&self) -> PakResult<HashMap<String, PakUntypedPointer>> {
108 let pointer = PakPointer::new_untyped(self.get_indices_start(), self.sizing.indices_size);
109 let buffer = self.source.borrow_mut().read(&pointer, 0)?;
110 let indices = bincode::deserialize(&buffer)?;
111 Ok(indices)
112 }
113
114 pub(crate) fn get_vault_start(&self) -> u64 {
115 24 + self.sizing.meta_size + self.sizing.indices_size + 8
117 }
118
119 pub(crate) fn get_indices_start(&self) -> u64 {
120 24 + self.sizing.meta_size
121 }
122
123}
124
125pub trait PakSource {
131 fn read(&mut self, pointer : &PakPointer, offset : u64) -> PakResult<Vec<u8>>;
133}
134
135impl <R> PakSource for R where R : Read + Seek {
136 fn read(&mut self, pointer : &PakPointer, offset : u64) -> PakResult<Vec<u8>> {
137 let mut buffer = vec![0u8; pointer.size() as usize];
138 self.seek(SeekFrom::Start(pointer.offset() + offset))?;
139 self.read_exact(&mut buffer)?;
140 Ok(buffer)
141 }
142}
143
144pub struct PakBuilder {
150 chunks : Vec<PakVaultReference>,
151 size_in_bytes : u64,
152 vault : Vec<u8>,
153 name: String,
154 description: String,
155 author: String,
156}
157
158impl PakBuilder {
159 pub fn new() -> Self {
161 Self {
162 vault : Vec::new(),
163 chunks : Vec::new(),
164 size_in_bytes : 0,
165 name: String::new(),
166 description: String::new(),
167 author: String::new(),
168 }
169 }
170
171 pub fn pak_no_search<T: PakItemSerialize>(&mut self, item : T) -> PakResult<PakPointer> {
173 let bytes = item.into_bytes()?;
174 let pointer = PakPointer::new_typed::<T>(self.size_in_bytes, bytes.len() as u64);
175 self.size_in_bytes += bytes.len() as u64;
176 self.vault.extend(bytes);
177 self.chunks.push(PakVaultReference { pointer: pointer.clone().into_typed::<T>(), indices: vec![] });
178 Ok(pointer)
179 }
180
181 pub fn pak<T : PakItemSerialize + PakItemSearchable>(&mut self, item : T) -> PakResult<PakPointer> {
183 let indices = item.get_indices();
184 let bytes = item.into_bytes()?;
185 let pointer = PakPointer::new_typed::<T>(self.size_in_bytes, bytes.len() as u64);
186 self.size_in_bytes += bytes.len() as u64;
187 self.vault.extend(bytes);
188 self.chunks.push(PakVaultReference { pointer: pointer.clone().into_typed::<T>(), indices: indices.clone() });
189 Ok(pointer)
190 }
191
192 pub fn size(&self) -> u64 {
194 self.size_in_bytes
195 }
196
197 pub fn len(&self) -> usize {
199 self.chunks.len()
200 }
201
202 pub fn with_name(mut self, name: &str) -> Self {
204 self.name = name.to_string();
205 self
206 }
207
208 pub fn with_description(mut self, description: &str) -> Self {
210 self.description = description.to_string();
211 self
212 }
213
214 pub fn with_author(mut self, author: &str) -> Self {
216 self.author = author.to_string();
217 self
218 }
219
220 pub fn set_name(&mut self, name: &str) {
222 self.name = name.to_string();
223 }
224
225 pub fn set_description(&mut self, description: &str) {
227 self.description = description.to_string();
228 }
229
230 pub fn set_author(&mut self, author: &str) {
232 self.author = author.to_string();
233 }
234
235 pub fn build_file(self, path : impl AsRef<Path>) -> PakResult<Pak> {
237 let (out, sizing, meta) = self.build_internal()?;
238
239 fs::write(&path, out)?;
240 let pak = Pak {
241 sizing,
242 meta,
243 source: RefCell::new(Box::new(BufReader::new(File::open(path)?))),
244 };
245 Ok(pak)
246 }
247
248 pub fn build_in_memory(self) -> PakResult<Pak> {
250 let (out, sizing, meta) = self.build_internal()?;
251
252 let pak = Pak {
253 sizing,
254 meta,
255 source: RefCell::new(Box::new(Cursor::new(out))),
256 };
257 Ok(pak)
258 }
259
260 fn build_internal(mut self) -> PakResult<(Vec<u8>, PakSizing, PakMeta)> {
261 let mut map : HashMap<String, PakTreeBuilder> = HashMap::new();
262 for chunk in &self.chunks {
263 for index in &chunk.indices{
264 map.entry(index.key.clone())
265 .or_insert(PakTreeBuilder::new(6))
266 .access()
267 .insert(index.value.clone(), chunk.pointer.clone())
268 ;
269 }
270 }
271
272 let mut pointer_map : HashMap<String, PakUntypedPointer> = HashMap::new();
273 for (key, tree) in map {
274 let pointer = tree.into_pak(&mut self)?;
275 pointer_map.insert(key, pointer.as_untyped());
276 }
277
278 let meta = PakMeta {
279 name: self.name,
280 description: self.description,
281 author: self.author,
282 version: "1.0".to_string(),
283 };
284
285 let sizing = PakSizing {
286 meta_size: bincode::serialized_size(&meta)?,
287 indices_size: bincode::serialized_size(&pointer_map)?,
288 vault_size: bincode::serialized_size(&self.vault)?,
289 };
290
291 let mut sizing_out = bincode::serialize(&sizing)?;
292 let mut meta_out = bincode::serialize(&meta)?;
293 let mut pointer_map_out = bincode::serialize(&pointer_map)?;
294 let mut vault_out = bincode::serialize(&self.vault)?;
295
296 let mut out = Vec::<u8>::new();
297 out.append(&mut sizing_out);
298 out.append(&mut meta_out);
299 out.append(&mut pointer_map_out);
300 out.append(&mut vault_out);
301 Ok((out, sizing, meta))
302 }
303
304}
305
306#[derive(Debug, Clone)]
311pub(crate) struct PakVaultReference {
312 pointer : PakTypedPointer,
313 indices : Vec<PakIndex>
314}