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
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 extra : Vec<u8>
21}
22
23impl PakBuilder {
24 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 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 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 pub fn size(&self) -> u64 {
60 self.size_in_bytes
61 }
62
63 pub fn len(&self) -> usize {
65 self.chunks.len()
66 }
67
68 pub fn with_name(mut self, name: &str) -> Self {
70 self.name = name.to_string();
71 self
72 }
73
74 pub fn with_description(mut self, description: &str) -> Self {
76 self.description = description.to_string();
77 self
78 }
79
80 pub fn with_author(mut self, author: &str) -> Self {
82 self.author = author.to_string();
83 self
84 }
85
86 pub fn set_name(&mut self, name: &str) {
88 self.name = name.to_string();
89 }
90
91 pub fn set_description(&mut self, description: &str) {
93 self.description = description.to_string();
94 }
95
96 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 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 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#[derive(Debug, Clone)]
197pub(crate) struct PakVaultReference {
198 pointer : PakPointer,
199 indices : Vec<PakIndex>
200}