pak_db/
builder.rs

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