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