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
8pub 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 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 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 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 pub fn size(&self) -> u64 {
63 self.size_in_bytes
64 }
65
66 pub fn len(&self) -> usize {
68 self.chunks.len()
69 }
70
71 pub fn with_name(mut self, name: &str) -> Self {
73 self.name = name.to_string();
74 self
75 }
76
77 pub fn with_description(mut self, description: &str) -> Self {
79 self.description = description.to_string();
80 self
81 }
82
83 pub fn with_author(mut self, author: &str) -> Self {
85 self.author = author.to_string();
86 self
87 }
88
89 pub fn set_name(&mut self, name: &str) {
91 self.name = name.to_string();
92 }
93
94 pub fn set_description(&mut self, description: &str) {
96 self.description = description.to_string();
97 }
98
99 pub fn set_author(&mut self, author: &str) {
101 self.author = author.to_string();
102 }
103
104 pub fn set_extra<T>(&mut self, value : &T) -> PakResult<()> where T : Serialize {
106 self.extra = bincode::serialize(value)?;
107 Ok(())
108 }
109
110 pub fn set_version(&mut self, version: String) {
112 self.version = version;
113 }
114
115 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 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#[derive(Debug, Clone)]
209pub(crate) struct PakVaultReference {
210 pointer : PakPointer,
211 indices : Vec<PakIndex>
212}