pak_db/
builder.rs

1use std::{collections::HashMap, fmt::Debug, fs::{self, File}, io::{BufReader, Cursor}, path::Path, sync::RwLock};
2
3
4use crate::{PAK_FILE_VERSION, Pak, btree::PakTreeBuilder, error::PakResult, index::PakIndex, item::{PakItemDeserialize, PakItemSearchable, PakItemSerialize}, meta::{PakMeta, PakSizing}, pointer::{PakPointer, PakUntypedPointer}};
5
6//==============================================================================================
7//        PakBuilder
8//==============================================================================================
9
10/// 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.
11pub struct PakBuilder {
12    chunks : Vec<PakVaultReference>,
13    size_in_bytes : u64,
14    vault : Vec<u8>,
15    name: String,
16    description: String,
17    author: String,
18}
19
20impl PakBuilder {
21    /// Creates a new instance of PakBuilder.
22    pub fn new() -> Self {
23        Self {
24            vault : Vec::new(),
25            chunks : Vec::new(),
26            size_in_bytes : 0,
27            name: String::new(),
28            description: String::new(),
29            author: String::new(),
30        }
31    }
32    
33    /// Adds an item to the pak file that does not support searching. Takes anything that implements [PakItemSerialize](crate::PakItemSerialize).
34    pub fn pak_no_search<T: PakItemSerialize + Debug + PakItemDeserialize>(&mut self, item : T) -> PakResult<PakPointer> {
35        let bytes = item.into_bytes()?;
36        let pointer = PakPointer::new_untyped(self.size_in_bytes, bytes.len() as u64);
37        self.size_in_bytes += bytes.len() as u64;
38        self.vault.extend(bytes);
39        self.chunks.push(PakVaultReference { pointer: pointer.clone(), indices: vec![] });
40        Ok(pointer)
41    }
42    
43    /// Adds an item to the pak file that supports searching. Takes anything that implements [PakItemSerialize](crate::PakItemSerialize) and [PakItemSearchable](crate::PakItemSearchable).
44    pub fn pak<T : PakItemSerialize + PakItemSearchable>(&mut self, item : T) -> PakResult<PakPointer> {
45        let indices = item.get_indices();
46        let bytes = item.into_bytes()?;
47        let pointer = PakPointer::new_typed::<T>(self.size_in_bytes, bytes.len() as u64);
48        self.size_in_bytes += bytes.len() as u64;
49        self.vault.extend(bytes);
50        self.chunks.push(PakVaultReference { pointer: pointer.clone(), indices: indices.clone() });
51        Ok(pointer)
52    }
53    
54    /// The current size of the pak file in bytes.
55    pub fn size(&self) -> u64 {
56        self.size_in_bytes
57    }
58    
59    /// The number of items in the pak file.
60    pub fn len(&self) -> usize {
61        self.chunks.len()
62    }
63    
64    /// Adds a name to the pak file's metadata.
65    pub fn with_name(mut self, name: &str) -> Self {
66        self.name = name.to_string();
67        self
68    }
69    
70    /// Adds a description to the pak file's metadata.
71    pub fn with_description(mut self, description: &str) -> Self {
72        self.description = description.to_string();
73        self
74    }
75    
76    /// Adds an author to the pak file's metadata.
77    pub fn with_author(mut self, author: &str) -> Self {
78        self.author = author.to_string();
79        self
80    }
81    
82    /// Sets the name of the pak file's metadata.
83    pub fn set_name(&mut self, name: &str) {
84        self.name = name.to_string();
85    }
86    
87    /// Sets the description of the pak file's metadata.
88    pub fn set_description(&mut self, description: &str) {
89        self.description = description.to_string();
90    }
91    
92    /// Sets the author of the pak file's metadata.
93    pub fn set_author(&mut self, author: &str) {
94        self.author = author.to_string();
95    }
96    
97    /// 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.
98    pub fn build_file(self, path : impl AsRef<Path>) -> PakResult<Pak> {
99        let (out, sizing, meta) = self.build_internal()?;
100        
101        fs::write(&path, out)?;
102        let pak  = Pak {
103            sizing,
104            meta,
105            source: RwLock::new(Box::new(BufReader::new(File::open(path)?))),
106        };
107        Ok(pak)
108    }
109    
110    /// 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.
111    pub fn build_in_memory(self) -> PakResult<Pak> {
112        let (out, sizing, meta) = self.build_internal()?;
113        
114        let pak = Pak {
115            sizing,
116            meta,
117            source: RwLock::new(Box::new(Cursor::new(out))),
118        };
119        Ok(pak)
120    }
121    
122    fn build_internal(mut self)  -> PakResult<(Vec<u8>, PakSizing, PakMeta)> {
123        let mut map : HashMap<String, PakTreeBuilder> = HashMap::new();
124        let mut list_values : HashMap<String, Vec<PakPointer>> = HashMap::new();
125        for chunk in &self.chunks {
126            for index in &chunk.indices{
127                map.entry(index.key.clone())
128                    .or_insert(PakTreeBuilder::new(16))
129                    .access()
130                    .insert(index.value.clone(), chunk.pointer.clone())
131                ;
132            }
133            let mut ptr = chunk.pointer.clone();
134            ptr.drop_type();
135            list_values.entry(chunk.pointer.type_name().to_string())
136                .or_insert(Vec::new())
137                .push(ptr);
138        }
139        
140        let mut pointer_map : HashMap<String, PakUntypedPointer> = HashMap::new();
141        for (key, tree) in map {
142            let pointer = tree.into_pak(&mut self)?;
143            pointer_map.insert(key, pointer.as_untyped());
144        }
145        
146        let mut lists = HashMap::<String, PakPointer>::new();
147        for (type_name, list) in list_values.into_iter() {
148            let pointer = self.pak_no_search(list)?;
149            lists.insert(type_name, pointer);
150        }
151        
152        let meta = PakMeta {
153            name: self.name,
154            description: self.description,
155            author: self.author,
156            version: PAK_FILE_VERSION.to_string(),
157        };
158        
159        let sizing = PakSizing {
160            meta_size: bincode::serialized_size(&meta)?,
161            indices_size: bincode::serialized_size(&pointer_map)?,
162            vault_size: bincode::serialized_size(&self.vault)?,
163            list_size: bincode::serialized_size(&lists)?,
164        };
165        
166        let mut sizing_out = bincode::serialize(&sizing)?;
167        let mut meta_out = bincode::serialize(&meta)?;
168        let mut pointer_map_out = bincode::serialize(&pointer_map)?;
169        let mut vault_out = bincode::serialize(&self.vault)?;
170        let mut list_out = bincode::serialize(&lists)?;
171        
172        let mut out = Vec::<u8>::new();
173        out.append(&mut sizing_out);
174        out.append(&mut meta_out);
175        out.append(&mut pointer_map_out);
176        out.append(&mut list_out);
177        out.append(&mut vault_out);
178        Ok((out, sizing, meta))
179    }
180}
181
182//==============================================================================================
183//        PakVaultReference
184//==============================================================================================
185
186#[derive(Debug, Clone)]
187pub(crate) struct PakVaultReference {
188    pointer : PakPointer,
189    indices : Vec<PakIndex>
190}