pak_db/
lib.rs

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
26//==============================================================================================
27//        Pak File
28//==============================================================================================
29
30/// Represents a Pak file. This struct provides access to the metadata and data stored within the Pak file.
31pub struct Pak {
32    sizing : PakSizing,
33    meta : PakMeta,
34    source : RefCell<Box<dyn PakSource>>
35}
36
37impl Pak {
38    /// Creates a new Pak instance from a [PakSource](crate::PakSource).
39    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    /// Loads a Pak from the specified file path. This will not load the entire pak file into memory, just the header.
52    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    /// Loads an object from the pak file via queried indices. This will only load the necessary data into memory.
58    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    /// Loads an object from the pak file via queried indices. This will only load the necessary data into memory.
64    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    /// Returns the size of the pak file in bytes.
70    pub fn size(&self) -> u64 {
71        24 + self.sizing.meta_size + self.sizing.indices_size + self.sizing.vault_size
72    }
73    
74    /// Returns the name given to the pak file.
75    pub fn name(&self) -> &str {
76        &self.meta.name
77    }
78    
79    /// Returns the version of the pak file.
80    pub fn version(&self) -> &str {
81        &self.meta.version
82    }
83    
84    /// Returns the author of the pak file.
85    pub fn author(&self) -> &str {
86        &self.meta.author
87    }
88    
89    /// Returns the description of the pak file.
90    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        // To be honest, I'm not sure why this start is offset by 8, it just is and I am to scared to ask.
122        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
131//==============================================================================================
132//        PakSource
133//==============================================================================================
134
135///This is where a Pak file will load from. This trait is automatically implemented for any type that implements [Read](std::io::Read) and [Seek](std::io::Seek).
136pub trait PakSource {
137    ///Returns data from the source based on a [PakPointer](crate::PakPointer)
138    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
150//==============================================================================================
151//        PakBuilder
152//==============================================================================================
153
154/// When it is time to create the pak file, this struct is used to build it. Remember that this struct doen't have the ability to read data that has been paked or delete data that has been paked.
155pub 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    /// Creates a new instance of PakBuilder.
166    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    /// Adds an item to the pak file that does not support searching. Takes anything that implements [PakItemSerialize](crate::PakItemSerialize).
178    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    /// Adds an item to the pak file that supports searching. Takes anything that implements [PakItemSerialize](crate::PakItemSerialize) and [PakItemSearchable](crate::PakItemSearchable).
188    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    /// The current size of the pak file in bytes.
199    pub fn size(&self) -> u64 {
200        self.size_in_bytes
201    }
202    
203    /// The number of items in the pak file.
204    pub fn len(&self) -> usize {
205        self.chunks.len()
206    }
207    
208    /// Adds a name to the pak file's metadata.
209    pub fn with_name(mut self, name: &str) -> Self {
210        self.name = name.to_string();
211        self
212    }
213    
214    /// Adds a description to the pak file's metadata.
215    pub fn with_description(mut self, description: &str) -> Self {
216        self.description = description.to_string();
217        self
218    }
219    
220    /// Adds an author to the pak file's metadata.
221    pub fn with_author(mut self, author: &str) -> Self {
222        self.author = author.to_string();
223        self
224    }
225    
226    /// Sets the name of the pak file's metadata.
227    pub fn set_name(&mut self, name: &str) {
228        self.name = name.to_string();
229    }
230    
231    /// Sets the description of the pak file's metadata.
232    pub fn set_description(&mut self, description: &str) {
233        self.description = description.to_string();
234    }
235    
236    /// Sets the author of the pak file's metadata.
237    pub fn set_author(&mut self, author: &str) {
238        self.author = author.to_string();
239    }
240    
241    /// Builds the pak file and writes it to the specified path. This also returns a [Pak](crate::Pak) object that is attached to that file.
242    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    /// Builds the pak file and writes it to the specified path. This also returns a [Pak](crate::Pak) object that is attached to that slice of memory.
255    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//==============================================================================================
313//        PakVaultReference
314//==============================================================================================
315
316#[derive(Debug, Clone)]
317pub(crate) struct PakVaultReference {
318    pointer : PakPointer,
319    indices : Vec<PakIndex>
320}