criware_utf_core/
writer.rs1use std::{any::type_name, borrow::Cow, collections::HashMap, io::Write};
2
3use crate::{Error, IOErrorHelper, Result, Value, ValueKind, value::sealed::Primitive};
4
5pub struct WriteContext(HashMap<&'static str, bool>);
20
21impl WriteContext {
22 pub fn new() -> Self {
26 WriteContext(HashMap::new())
27 }
28 pub fn is_included(&self, column_name: &str) -> bool {
33 match self.0.get(column_name) {
34 Some(v) => *v,
35 None => true,
36 }
37 }
38 pub fn set_inclusion_state(&mut self, column_name: &'static str, included: bool) {
43 self.0.insert(column_name, included);
44 }
45}
46
47pub struct Writer<'a> {
50 column_data: Vec<u8>,
51 row_data: Vec<u8>,
52 strings: HashMap<Cow<'a, str>, u32>,
53 string_data: Vec<u8>,
54 blobs: Vec<u8>,
55 field_count: u16,
56}
57
58impl<'a> Writer<'a> {
59 pub fn new(table_name: &'a str) -> Writer<'a> {
69 let mut writer = Writer {
70 column_data: Vec::new(),
71 row_data: Vec::new(),
72 strings: HashMap::new(),
73 string_data: Vec::new(),
74 blobs: Vec::new(),
75 field_count: 0,
76 };
77 writer.strings.insert(Cow::Borrowed("<NULL>"), 0);
78 writer.strings.insert(Cow::Borrowed(table_name), 7);
79 writer.string_data.extend_from_slice(b"<NULL>\0");
80 writer.string_data.extend_from_slice(table_name.as_bytes());
81 writer.string_data.push(0u8);
82 writer
83 }
84
85 pub fn end(&self, writer: &mut dyn Write, row_size: u16, row_count: u32) -> Result<()> {
100 if self.row_data.len() != (row_size as usize) * (row_count as usize) {
101 return Err(Error::MalformedHeader);
102 }
103 let zeroes = [0u8; 8];
104 let row_offset = self.column_data.len() as u32 + 24;
105 let string_offset = row_offset + self.row_data.len() as u32;
106 let mut blob_offset = string_offset + self.string_data.len() as u32;
107 let blob_offset_remainder = 8 - (blob_offset & 7);
108 blob_offset += blob_offset_remainder;
109 let table_name: u32 = 7;
110 let table_size = blob_offset + self.blobs.len() as u32;
111 writer.write_all(b"@UTF").io("@UTF header")?;
112 writer
113 .write_all(&table_size.to_be_bytes())
114 .io("@UTF header")?;
115 writer
116 .write_all(&row_offset.to_be_bytes())
117 .io("@UTF header")?;
118 writer
119 .write_all(&string_offset.to_be_bytes())
120 .io("@UTF header")?;
121 writer
122 .write_all(&blob_offset.to_be_bytes())
123 .io("@UTF header")?;
124 writer
125 .write_all(&table_name.to_be_bytes())
126 .io("@UTF header")?;
127 writer
128 .write_all(&self.field_count.to_be_bytes())
129 .io("@UTF header")?;
130 writer
131 .write_all(&row_size.to_be_bytes())
132 .io("@UTF header")?;
133 writer
134 .write_all(&row_count.to_be_bytes())
135 .io("@UTF header")?;
136 writer.write_all(&self.column_data).io("UTF column data")?;
137 writer.write_all(&self.row_data).io("UTF row data")?;
138 writer.write_all(&self.string_data).io("UTF string data")?;
139 writer
140 .write_all(&zeroes[0..(blob_offset_remainder as usize)])
141 .io("UTF string data")?;
142 writer.write_all(&self.blobs).io("UTF blobs")?;
143 Ok(())
144 }
145
146 fn push_constant_column_private<T: Value>(
147 &mut self,
148 name: &'a str,
149 value: Option<&'a T>,
150 ) -> Result<()> {
151 let flag = if value.is_some() { 0x30 } else { 0x10 };
152 self.write_primitive::<u8>(false, Cow::Owned(flag | (T::Primitive::TYPE_FLAG as u8)));
153 self.write_primitive(false, Cow::Borrowed(name));
154 if let Some(value) = value {
155 self.write_value(false, value)?;
156 }
157 self.field_count += 1;
158 Ok(())
159 }
160
161 pub fn push_constant_column<T: Value>(&mut self, name: &'a str, value: &'a T) -> Result<()> {
177 self.push_constant_column_private(name, Some(value))
178 }
179
180 pub fn push_constant_column_opt<T: Value>(
192 &mut self,
193 name: &'a str,
194 value: &'a Option<T>,
195 ) -> Result<()> {
196 self.push_constant_column_private::<T>(name, value.into())
197 }
198
199 fn push_rowed_column_private(&mut self, name: &'a str, included: bool, kind: ValueKind) {
200 let storage_flag = if included { 0x50 } else { 0x10 };
201 self.write_primitive::<u8>(false, Cow::Owned(storage_flag | (kind as u8)));
202 self.write_primitive::<str>(false, Cow::Borrowed(name));
203 self.field_count += 1;
204 }
205
206 pub fn push_rowed_column<T: Value>(&mut self, name: &'a str) {
217 self.push_rowed_column_private(name, true, T::Primitive::TYPE_FLAG)
218 }
219
220 pub fn push_rowed_column_opt<T: Value>(&mut self, name: &'a str, included: bool) {
233 self.push_rowed_column_private(name, included, T::Primitive::TYPE_FLAG)
234 }
235
236 fn write_primitive<T: Primitive + ?Sized>(&mut self, rowed: bool, value: Cow<'a, T>) {
237 let destination = if rowed {
238 &mut self.row_data
239 } else {
240 &mut self.column_data
241 };
242 destination.extend_from_slice(
243 T::write(
244 value,
245 &mut self.strings,
246 &mut self.string_data,
247 &mut self.blobs,
248 )
249 .as_ref(),
250 );
251 }
252
253 pub fn write_value<T: Value>(&mut self, rowed: bool, value: &'a T) -> Result<()> {
268 match T::to_primitive(value) {
269 Ok(prim) => {
270 self.write_primitive(rowed, prim);
271 Ok(())
272 }
273 Err(error) => {
274 return Err(Error::ValueConversion(
275 type_name::<T>(),
276 type_name::<T::Primitive>(),
277 error,
278 ));
279 }
280 }
281 }
282}