pak_db/
builder.rs

1use std::{collections::HashMap, fmt::Debug, fs::{self, File}, io::{BufReader, Cursor}, path::Path, sync::{Arc, RwLock}};
2
3
4use serde::Serialize;
5
6use crate::{PAK_FILE_VERSION, Pak, PakInner, 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 inner = Arc::new(PakInner {
121            sizing,
122            meta,
123            source: RwLock::new(Box::new(BufReader::new(File::open(path)?))),
124        });
125        let pak  = Pak { inner };
126        Ok(pak)
127    }
128    
129    /// 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.
130    pub fn build_in_memory(self) -> PakResult<Pak> {
131        let (out, sizing, meta) = self.build_internal()?;
132        let inner = Arc::new(PakInner {
133            sizing,
134            meta,
135            source: RwLock::new(Box::new(Cursor::new(out))),
136        });
137        let pak = Pak { inner };
138        Ok(pak)
139    }
140    
141    fn build_internal(mut self)  -> PakResult<(Vec<u8>, PakSizing, PakMeta)> {
142        let mut map : HashMap<String, PakTreeBuilder> = HashMap::new();
143        let mut list_values : HashMap<String, Vec<PakPointer>> = HashMap::new();
144        for chunk in &self.chunks {
145            for index in &chunk.indices{
146                map.entry(index.key.clone())
147                    .or_insert(PakTreeBuilder::new(16))
148                    .access()
149                    .insert(index.value.clone(), chunk.pointer.clone())
150                ;
151            }
152            let mut ptr = chunk.pointer.clone();
153            ptr.drop_type();
154            list_values.entry(chunk.pointer.type_name().to_string())
155                .or_insert(Vec::new())
156                .push(ptr);
157        }
158        
159        let mut pointer_map : HashMap<String, PakUntypedPointer> = HashMap::new();
160        for (key, tree) in map {
161            let pointer = tree.into_pak(&mut self)?;
162            pointer_map.insert(key, pointer.as_untyped());
163        }
164        
165        let mut lists = HashMap::<String, PakPointer>::new();
166        for (type_name, list) in list_values.into_iter() {
167            let pointer = self.pak_no_search(&list)?;
168            lists.insert(type_name, pointer);
169        }
170        
171        let hash = sha256::digest(self.vault.as_slice());
172        let meta = PakMeta {
173            identifier : format!("{hash}|{}|{}", self.name, self.version),
174            name: self.name,
175            description: self.description,
176            author: self.author,
177            version: self.version,
178            pak_version: PAK_FILE_VERSION.to_string(),
179            extra : self.extra
180        };
181        
182        
183        let sizing = PakSizing {
184            meta_size: bincode::serialized_size(&meta)?,
185            indices_size: bincode::serialized_size(&pointer_map)?,
186            vault_size: bincode::serialized_size(&self.vault)?,
187            list_size: bincode::serialized_size(&lists)?,
188        };
189        
190        let mut sizing_out = bincode::serialize(&sizing)?;
191        let mut meta_out = bincode::serialize(&meta)?;
192        let mut pointer_map_out = bincode::serialize(&pointer_map)?;
193        let mut vault_out = bincode::serialize(&self.vault)?;
194        let mut list_out = bincode::serialize(&lists)?;
195        
196        let mut out = Vec::<u8>::new();
197        out.append(&mut sizing_out);
198        out.append(&mut meta_out);
199        out.append(&mut pointer_map_out);
200        out.append(&mut list_out);
201        out.append(&mut vault_out);
202        Ok((out, sizing, meta))
203    }
204}
205
206//==============================================================================================
207//        PakVaultReference
208//==============================================================================================
209
210#[derive(Debug, Clone)]
211pub(crate) struct PakVaultReference {
212    pointer : PakPointer,
213    indices : Vec<PakIndex>
214}