1use crate::soch::{SochValue}; use std::collections::HashMap;
33use toon_format::{self, EncodeOptions, DecodeOptions, Delimiter, Indent};
34use toon_format::types::KeyFoldingMode;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42#[repr(u8)]
43pub enum SochTypeTag {
44 Null = 0x00,
46 False = 0x01,
48 True = 0x02,
50 PosFixint = 0x10,
52 NegFixint = 0x20,
54 Int8 = 0x30,
56 Int16 = 0x31,
58 Int32 = 0x32,
60 Int64 = 0x33,
62 Float32 = 0x40,
64 Float64 = 0x41,
66 FixStr = 0x50,
68 Str8 = 0x60,
70 Str16 = 0x61,
72 Str32 = 0x62,
74 Array = 0x70,
76 Ref = 0x80,
78 Object = 0x90,
80 Binary = 0xA0,
82 UInt = 0xB0,
84}
85
86#[derive(Debug, Clone)]
92pub struct SochDocument {
93 pub root: SochValue,
95 pub version: u32,
97}
98
99impl SochDocument {
100 pub fn new(root: SochValue) -> Self {
102 Self {
103 root,
104 version: 1,
105 }
106 }
107
108 pub fn new_table(_name: impl Into<String>, fields: Vec<String>, rows: Vec<Vec<SochValue>>) -> Self {
110 let fields_str: Vec<String> = fields;
112 let mut array = Vec::new();
113 for row in rows {
114 let mut obj = HashMap::new();
115 for (i, val) in row.into_iter().enumerate() {
116 if i < fields_str.len() {
117 obj.insert(fields_str[i].clone(), val);
118 }
119 }
120 array.push(SochValue::Object(obj));
121 }
122
123 Self {
124 root: SochValue::Array(array),
125 version: 1,
126 }
127 }
128}
129
130pub struct SochTextEncoder;
136
137impl SochTextEncoder {
138 pub fn encode(doc: &SochDocument) -> String {
140 let options = EncodeOptions::new()
142 .with_indent(Indent::Spaces(2))
143 .with_delimiter(Delimiter::Comma)
144 .with_key_folding(KeyFoldingMode::Safe);
145
146 toon_format::encode(&doc.root, &options).unwrap_or_else(|e| format!("Error encoding TOON: {}", e))
149 }
150}
151
152pub struct SochTextParser;
154
155impl SochTextParser {
156 pub fn parse(input: &str) -> Result<SochDocument, SochParseError> {
157 Self::parse_with_options(input, DecodeOptions::default())
158 }
159
160 pub fn parse_with_options(input: &str, options: DecodeOptions) -> Result<SochDocument, SochParseError> {
161 let root: SochValue = toon_format::decode(input, &options)
162 .map_err(|e| SochParseError::RowError { line: 0, cause: e.to_string() })?;
163
164 Ok(SochDocument::new(root))
165 }
166
167 pub fn parse_header(_line: &str) -> Result<(String, usize, Vec<String>), SochParseError> {
169 Err(SochParseError::InvalidHeader)
170 }
171}
172
173pub struct SochTokenCounter;
175impl SochTokenCounter {
176 pub fn count(_doc: &SochDocument) -> usize {
177 0
178 }
179}
180
181
182#[derive(Debug, Clone)]
184pub enum SochParseError {
185 EmptyInput,
186 InvalidHeader,
187 InvalidRowCount,
188 InvalidValue,
189 RowCountMismatch { expected: usize, actual: usize },
190 FieldCountMismatch { expected: usize, actual: usize },
191 RowError { line: usize, cause: String },
192}
193
194impl std::fmt::Display for SochParseError {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 write!(f, "{:?}", self)
197 }
198}
199impl std::error::Error for SochParseError {}
200
201
202pub const TOON_MAGIC: [u8; 4] = [0x54, 0x4F, 0x4F, 0x4E]; pub struct SochDbBinaryCodec;
211
212impl SochDbBinaryCodec {
213 pub fn encode(doc: &SochDocument) -> Vec<u8> {
215 let mut buf = Vec::new();
216 buf.extend_from_slice(&TOON_MAGIC);
217 Self::write_varint(&mut buf, doc.version as u64);
219 Self::write_value(&mut buf, &doc.root);
221 let checksum = crc32fast::hash(&buf);
223 buf.extend_from_slice(&checksum.to_le_bytes());
224 buf
225 }
226
227 pub fn decode(data: &[u8]) -> Result<SochDocument, SochParseError> {
229 if data.len() < 8 { return Err(SochParseError::InvalidHeader); }
230 if data[0..4] != TOON_MAGIC { return Err(SochParseError::InvalidHeader); }
231
232 let stored_checksum = u32::from_le_bytes(data[data.len() - 4..].try_into().unwrap());
234 let computed_checksum = crc32fast::hash(&data[..data.len() - 4]);
235 if stored_checksum != computed_checksum { return Err(SochParseError::InvalidValue); }
236
237 let data = &data[..data.len() - 4];
238 let mut cursor = 4;
239
240 let (version, bytes) = Self::read_varint(&data[cursor..])?;
241 cursor += bytes;
242
243 let (root, _) = Self::read_value(&data[cursor..])?;
244
245 Ok(SochDocument {
246 root,
247 version: version as u32,
248 })
249 }
250
251 fn write_varint(buf: &mut Vec<u8>, mut n: u64) {
252 while n > 127 {
253 buf.push((n as u8 & 0x7F) | 0x80);
254 n >>= 7;
255 }
256 buf.push(n as u8 & 0x7F);
257 }
258
259 fn read_varint(data: &[u8]) -> Result<(u64, usize), SochParseError> {
260 let mut result: u64 = 0;
261 let mut shift = 0;
262 let mut i = 0;
263 while i < data.len() {
264 let byte = data[i];
265 result |= ((byte & 0x7F) as u64) << shift;
266 i += 1;
267 if byte & 0x80 == 0 { return Ok((result, i)); }
268 shift += 7;
269 }
270 Err(SochParseError::InvalidValue)
271 }
272
273 fn read_string(data: &[u8]) -> Result<(String, usize), SochParseError> {
274 let (len, varint_bytes) = Self::read_varint(data)?;
275 let len = len as usize;
276 if data.len() < varint_bytes + len { return Err(SochParseError::InvalidValue); }
277 let s = std::str::from_utf8(&data[varint_bytes..varint_bytes+len]).map_err(|_| SochParseError::InvalidValue)?.to_string();
278 Ok((s, varint_bytes + len))
279 }
280
281 fn write_value(buf: &mut Vec<u8>, value: &SochValue) {
282 match value {
283 SochValue::Null => buf.push(SochTypeTag::Null as u8),
284 SochValue::Bool(true) => buf.push(SochTypeTag::True as u8),
285 SochValue::Bool(false) => buf.push(SochTypeTag::False as u8),
286 SochValue::Int(n) => {
287 buf.push(SochTypeTag::Int64 as u8);
289 buf.extend_from_slice(&n.to_le_bytes());
290 },
291 SochValue::UInt(n) => {
292 buf.push(SochTypeTag::UInt as u8);
293 Self::write_varint(buf, *n);
294 },
295 SochValue::Float(f) => {
296 buf.push(SochTypeTag::Float64 as u8);
297 buf.extend_from_slice(&f.to_le_bytes());
298 },
299 SochValue::Text(s) => {
300 buf.push(SochTypeTag::Str32 as u8);
301 let bytes = s.as_bytes();
302 buf.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
303 buf.extend_from_slice(bytes);
304 },
305 SochValue::Binary(b) => {
306 buf.push(SochTypeTag::Binary as u8);
307 Self::write_varint(buf, b.len() as u64);
308 buf.extend_from_slice(b);
309 },
310 SochValue::Array(arr) => {
311 buf.push(SochTypeTag::Array as u8);
312 Self::write_varint(buf, arr.len() as u64);
313 for item in arr { Self::write_value(buf, item); }
314 },
315 SochValue::Object(map) => {
316 buf.push(SochTypeTag::Object as u8);
317 Self::write_varint(buf, map.len() as u64);
318 for (k, v) in map {
319 let k_bytes = k.as_bytes();
321 Self::write_varint(buf, k_bytes.len() as u64);
322 buf.extend_from_slice(k_bytes);
323 Self::write_value(buf, v);
325 }
326 },
327 SochValue::Ref { table, id } => {
328 buf.push(SochTypeTag::Ref as u8);
329 let t_bytes = table.as_bytes();
331 Self::write_varint(buf, t_bytes.len() as u64);
332 buf.extend_from_slice(t_bytes);
333 Self::write_varint(buf, *id);
335 }
336 }
337 }
338
339 fn read_value(data: &[u8]) -> Result<(SochValue, usize), SochParseError> {
340 if data.is_empty() { return Err(SochParseError::InvalidValue); }
341 let tag = data[0];
342 let mut cursor = 1;
343
344 match tag {
345 0x00 => Ok((SochValue::Null, 1)),
346 0x01 => Ok((SochValue::Bool(false), 1)),
347 0x02 => Ok((SochValue::Bool(true), 1)),
348 0x33 => { if data.len() < cursor + 8 { return Err(SochParseError::InvalidValue); }
350 let n = i64::from_le_bytes(data[cursor..cursor+8].try_into().unwrap());
351 Ok((SochValue::Int(n), cursor+8))
352 },
353 0x41 => { if data.len() < cursor + 8 { return Err(SochParseError::InvalidValue); }
355 let f = f64::from_le_bytes(data[cursor..cursor+8].try_into().unwrap());
356 Ok((SochValue::Float(f), cursor+8))
357 },
358 0x62 => { if data.len() < cursor + 4 { return Err(SochParseError::InvalidValue); }
360 let len = u32::from_le_bytes(data[cursor..cursor+4].try_into().unwrap()) as usize;
361 cursor += 4;
362 if data.len() < cursor + len { return Err(SochParseError::InvalidValue); }
363 let s = std::str::from_utf8(&data[cursor..cursor+len]).unwrap().to_string();
364 Ok((SochValue::Text(s), cursor+len))
365 },
366 0x70 => { let (len, bytes) = Self::read_varint(&data[cursor..])?;
368 cursor += bytes;
369 let mut arr = Vec::new();
370 for _ in 0..len {
371 let (val, bytes_read) = Self::read_value(&data[cursor..])?;
372 cursor += bytes_read;
373 arr.push(val);
374 }
375 Ok((SochValue::Array(arr), cursor))
376 },
377 0xB0 => { let (n, bytes) = Self::read_varint(&data[cursor..])?;
379 Ok((SochValue::UInt(n), cursor+bytes))
380 },
381 0x80 => { let (table, table_bytes) = Self::read_string(&data[cursor..])?;
383 cursor += table_bytes;
384 let (id, id_bytes) = Self::read_varint(&data[cursor..])?;
385 Ok((SochValue::Ref { table, id }, cursor+id_bytes))
386 },
387 0x90 => { let (len, bytes_read) = Self::read_varint(&data[cursor..])?;
389 cursor += bytes_read;
390 let mut map = HashMap::new();
391 for _ in 0..len {
392 let (k, k_bytes) = Self::read_string(&data[cursor..])?;
393 cursor += k_bytes;
394 let (v, v_bytes) = Self::read_value(&data[cursor..])?;
395 cursor += v_bytes;
396 map.insert(k, v);
397 }
398 Ok((SochValue::Object(map), cursor))
399 },
400 _ => Err(SochParseError::InvalidValue)
402 }
403 }
404}
405
406#[cfg(test)]
407mod tests {
408 use super::*;
409
410 #[test]
411 fn test_simple_object() {
412 let mut obj = HashMap::new();
413 obj.insert("id".to_string(), SochValue::Int(1));
414 obj.insert("name".to_string(), SochValue::Text("Alice".to_string()));
415 let doc = SochDocument::new(SochValue::Object(obj));
416
417 let encoded = SochTextEncoder::encode(&doc);
419 assert!(encoded.contains("id"));
421 assert!(encoded.contains("1"));
422 assert!(encoded.contains("name"));
423 assert!(encoded.contains("Alice"));
424
425 let bin = SochDbBinaryCodec::encode(&doc);
427 let decoded = SochDbBinaryCodec::decode(&bin).unwrap();
428 if let SochValue::Object(map) = decoded.root {
429 assert_eq!(map.get("id"), Some(&SochValue::Int(1)));
432 assert_eq!(map.get("name"), Some(&SochValue::Text("Alice".to_string())));
433 } else {
434 panic!("Expected object");
435 }
436 }
437
438 #[test]
439 fn test_array() {
440 let arr = vec![
441 SochValue::Int(1),
442 SochValue::Int(2),
443 ];
444 let doc = SochDocument::new(SochValue::Array(arr));
445
446 let encoded = SochTextEncoder::encode(&doc);
447 assert!(encoded.contains("1"));
449 assert!(encoded.contains("2"));
450
451 let bin = SochDbBinaryCodec::encode(&doc);
452 let decoded = SochDbBinaryCodec::decode(&bin).unwrap();
453 if let SochValue::Array(arr) = decoded.root {
454 assert_eq!(arr.len(), 2);
455 assert_eq!(arr[0], SochValue::Int(1));
456 } else {
457 panic!("Expected array");
458 }
459 }
460}