1pub mod binary_packer;
2pub mod binary_unpacker;
3pub mod display96;
4pub mod frame;
5pub mod parsers;
6pub mod serializers;
7pub mod types;
8
9pub use binary_packer::pack;
11pub use binary_unpacker::unpack;
12pub use types::SchemaError;
13
14pub fn encode_schema(json: &str) -> Result<String, SchemaError> {
23 use parsers::{InputParser, JsonParser};
24
25 let ir = JsonParser::parse(json)?;
26 let binary = pack(&ir);
27 Ok(frame::encode_framed(&binary))
28}
29
30pub fn decode_schema(encoded: &str) -> Result<String, SchemaError> {
38 use serializers::{JsonSerializer, OutputSerializer};
39
40 let binary = frame::decode_framed(encoded)?;
41 let ir = unpack(&binary)?;
42 JsonSerializer::serialize(&ir)
43}
44
45#[cfg(test)]
46mod integration_tests {
47 use super::*;
48 use crate::encoders::algorithms::schema::types::{
49 FLAG_HAS_NULLS, FLAG_HAS_ROOT_KEY, FieldDef, FieldType, IntermediateRepresentation,
50 SchemaHeader, SchemaValue,
51 };
52 use parsers::{InputParser, JsonParser};
53 use serializers::{JsonSerializer, OutputSerializer};
54
55 #[test]
56 fn test_round_trip_simple() {
57 let fields = vec![
58 FieldDef::new("id", FieldType::U64),
59 FieldDef::new("name", FieldType::String),
60 ];
61 let header = SchemaHeader::new(2, fields);
62
63 let values = vec![
64 SchemaValue::U64(1),
65 SchemaValue::String("Alice".to_string()),
66 SchemaValue::U64(2),
67 SchemaValue::String("Bob".to_string()),
68 ];
69
70 let original = IntermediateRepresentation::new(header, values).unwrap();
71
72 let packed = pack(&original);
74 let unpacked = unpack(&packed).unwrap();
75
76 assert_eq!(original, unpacked);
77 }
78
79 #[test]
80 fn test_round_trip_all_types() {
81 let fields = vec![
82 FieldDef::new("u64_field", FieldType::U64),
83 FieldDef::new("i64_field", FieldType::I64),
84 FieldDef::new("f64_field", FieldType::F64),
85 FieldDef::new("string_field", FieldType::String),
86 FieldDef::new("bool_field", FieldType::Bool),
87 ];
88 let header = SchemaHeader::new(1, fields);
89
90 let values = vec![
91 SchemaValue::U64(42),
92 SchemaValue::I64(-42),
93 SchemaValue::F64(std::f64::consts::PI),
94 SchemaValue::String("test".to_string()),
95 SchemaValue::Bool(true),
96 ];
97
98 let original = IntermediateRepresentation::new(header, values).unwrap();
99
100 let packed = pack(&original);
101 let unpacked = unpack(&packed).unwrap();
102
103 assert_eq!(original, unpacked);
104 }
105
106 #[test]
107 fn test_round_trip_with_root_key() {
108 let mut header = SchemaHeader::new(1, vec![FieldDef::new("id", FieldType::U64)]);
109 header.root_key = Some("users".to_string());
110 header.set_flag(FLAG_HAS_ROOT_KEY);
111
112 let values = vec![SchemaValue::U64(42)];
113 let original = IntermediateRepresentation::new(header, values).unwrap();
114
115 let packed = pack(&original);
116 let unpacked = unpack(&packed).unwrap();
117
118 assert_eq!(original, unpacked);
119 }
120
121 #[test]
122 fn test_round_trip_with_nulls() {
123 let mut header = SchemaHeader::new(
124 2,
125 vec![
126 FieldDef::new("id", FieldType::U64),
127 FieldDef::new("name", FieldType::String),
128 ],
129 );
130
131 let total_values: usize = 2 * 2; let bitmap_bytes = total_values.div_ceil(8); let mut null_bitmap = vec![0u8; bitmap_bytes];
135 null_bitmap[0] |= 1 << 1; header.null_bitmap = Some(null_bitmap);
138 header.set_flag(FLAG_HAS_NULLS);
139
140 let values = vec![
141 SchemaValue::U64(1),
142 SchemaValue::Null, SchemaValue::U64(2),
144 SchemaValue::String("Bob".to_string()),
145 ];
146
147 let original = IntermediateRepresentation::new(header, values).unwrap();
148
149 let packed = pack(&original);
150 let unpacked = unpack(&packed).unwrap();
151
152 assert_eq!(original, unpacked);
153 }
154
155 #[test]
156 fn test_round_trip_array() {
157 let fields = vec![FieldDef::new(
158 "tags",
159 FieldType::Array(Box::new(FieldType::U64)),
160 )];
161 let header = SchemaHeader::new(1, fields);
162
163 let values = vec![SchemaValue::Array(vec![
164 SchemaValue::U64(1),
165 SchemaValue::U64(2),
166 SchemaValue::U64(3),
167 ])];
168
169 let original = IntermediateRepresentation::new(header, values).unwrap();
170
171 let packed = pack(&original);
172 let unpacked = unpack(&packed).unwrap();
173
174 assert_eq!(original, unpacked);
175 }
176
177 #[test]
178 fn test_round_trip_large_values() {
179 let fields = vec![
180 FieldDef::new("large_u64", FieldType::U64),
181 FieldDef::new("large_i64", FieldType::I64),
182 ];
183 let header = SchemaHeader::new(1, fields);
184
185 let values = vec![SchemaValue::U64(u64::MAX), SchemaValue::I64(i64::MIN)];
186
187 let original = IntermediateRepresentation::new(header, values).unwrap();
188
189 let packed = pack(&original);
190 let unpacked = unpack(&packed).unwrap();
191
192 assert_eq!(original, unpacked);
193 }
194
195 #[test]
196 fn test_round_trip_empty_string() {
197 let fields = vec![FieldDef::new("name", FieldType::String)];
198 let header = SchemaHeader::new(1, fields);
199
200 let values = vec![SchemaValue::String("".to_string())];
201
202 let original = IntermediateRepresentation::new(header, values).unwrap();
203
204 let packed = pack(&original);
205 let unpacked = unpack(&packed).unwrap();
206
207 assert_eq!(original, unpacked);
208 }
209
210 #[test]
211 fn test_round_trip_multiple_rows() {
212 let fields = vec![
213 FieldDef::new("id", FieldType::U64),
214 FieldDef::new("score", FieldType::F64),
215 FieldDef::new("active", FieldType::Bool),
216 ];
217 let header = SchemaHeader::new(3, fields);
218
219 let values = vec![
220 SchemaValue::U64(1),
221 SchemaValue::F64(95.5),
222 SchemaValue::Bool(true),
223 SchemaValue::U64(2),
224 SchemaValue::F64(87.3),
225 SchemaValue::Bool(false),
226 SchemaValue::U64(3),
227 SchemaValue::F64(92.1),
228 SchemaValue::Bool(true),
229 ];
230
231 let original = IntermediateRepresentation::new(header, values).unwrap();
232
233 let packed = pack(&original);
234 let unpacked = unpack(&packed).unwrap();
235
236 assert_eq!(original, unpacked);
237 }
238
239 #[test]
240 fn test_invalid_data() {
241 let result = unpack(&[]);
243 assert!(matches!(result, Err(SchemaError::UnexpectedEndOfData)));
244
245 let result = unpack(&[0, 1, 2]);
247 assert!(result.is_err());
248 }
249
250 #[test]
251 fn test_json_full_roundtrip() {
252 let input = r#"{"users":[{"id":1,"name":"alice"},{"id":2,"name":"bob"}]}"#;
253 let ir = JsonParser::parse(input).unwrap();
254 let binary = pack(&ir);
255 let ir2 = unpack(&binary).unwrap();
256 let output = JsonSerializer::serialize(&ir2).unwrap();
257
258 let input_value: serde_json::Value = serde_json::from_str(input).unwrap();
260 let output_value: serde_json::Value = serde_json::from_str(&output).unwrap();
261 assert_eq!(input_value, output_value);
262 }
263
264 #[test]
265 fn test_json_simple_object() {
266 let input = r#"{"id":1,"name":"alice","score":95.5}"#;
267 let ir = JsonParser::parse(input).unwrap();
268 let binary = pack(&ir);
269 let ir2 = unpack(&binary).unwrap();
270 let output = JsonSerializer::serialize(&ir2).unwrap();
271
272 let input_value: serde_json::Value = serde_json::from_str(input).unwrap();
273 let output_value: serde_json::Value = serde_json::from_str(&output).unwrap();
274 assert_eq!(input_value, output_value);
275 }
276
277 #[test]
278 fn test_json_nested_objects() {
279 let input = r#"{"user":{"profile":{"name":"alice","age":30}}}"#;
280 let ir = JsonParser::parse(input).unwrap();
281 let binary = pack(&ir);
282 let ir2 = unpack(&binary).unwrap();
283 let output = JsonSerializer::serialize(&ir2).unwrap();
284
285 let input_value: serde_json::Value = serde_json::from_str(input).unwrap();
286 let output_value: serde_json::Value = serde_json::from_str(&output).unwrap();
287 assert_eq!(input_value, output_value);
288 }
289
290 #[test]
291 fn test_json_with_nulls() {
292 let input = r#"{"name":"alice","age":null,"active":true}"#;
293 let ir = JsonParser::parse(input).unwrap();
294 assert!(ir.header.has_flag(FLAG_HAS_NULLS));
295
296 let binary = pack(&ir);
297 let ir2 = unpack(&binary).unwrap();
298 let output = JsonSerializer::serialize(&ir2).unwrap();
299
300 let input_value: serde_json::Value = serde_json::from_str(input).unwrap();
301 let output_value: serde_json::Value = serde_json::from_str(&output).unwrap();
302 assert_eq!(input_value, output_value);
303 }
304
305 #[test]
306 fn test_json_with_arrays() {
307 let input = r#"{"scores":[95,87,92],"tags":["rust","json"]}"#;
308 let ir = JsonParser::parse(input).unwrap();
309 let binary = pack(&ir);
310 let ir2 = unpack(&binary).unwrap();
311 let output = JsonSerializer::serialize(&ir2).unwrap();
312
313 let input_value: serde_json::Value = serde_json::from_str(input).unwrap();
314 let output_value: serde_json::Value = serde_json::from_str(&output).unwrap();
315 assert_eq!(input_value, output_value);
316 }
317
318 #[test]
319 fn test_encode_schema_roundtrip() {
320 let input = r#"{"users":[{"id":1,"name":"alice"},{"id":2,"name":"bob"}]}"#;
321 let encoded = encode_schema(input).unwrap();
322
323 assert!(encoded.starts_with(frame::FRAME_START));
325 assert!(encoded.ends_with(frame::FRAME_END));
326
327 let decoded = decode_schema(&encoded).unwrap();
329
330 let input_value: serde_json::Value = serde_json::from_str(input).unwrap();
332 let output_value: serde_json::Value = serde_json::from_str(&decoded).unwrap();
333 assert_eq!(input_value, output_value);
334 }
335
336 #[test]
337 fn test_encode_schema_simple() {
338 let input = r#"{"id":1,"name":"alice","score":95.5}"#;
339 let encoded = encode_schema(input).unwrap();
340 let decoded = decode_schema(&encoded).unwrap();
341
342 let input_value: serde_json::Value = serde_json::from_str(input).unwrap();
343 let output_value: serde_json::Value = serde_json::from_str(&decoded).unwrap();
344 assert_eq!(input_value, output_value);
345 }
346
347 #[test]
348 fn test_encode_schema_with_nulls() {
349 let input = r#"{"name":"alice","age":null,"active":true}"#;
350 let encoded = encode_schema(input).unwrap();
351 let decoded = decode_schema(&encoded).unwrap();
352
353 let input_value: serde_json::Value = serde_json::from_str(input).unwrap();
354 let output_value: serde_json::Value = serde_json::from_str(&decoded).unwrap();
355 assert_eq!(input_value, output_value);
356 }
357
358 #[test]
359 fn test_encode_schema_empty_object() {
360 let input = r#"{}"#;
361 let result = encode_schema(input);
362 println!("Empty object result: {:?}", result);
365 }
366
367 #[test]
368 fn test_decode_schema_invalid_frame() {
369 let invalid = "not_framed_data";
370 let result = decode_schema(invalid);
371 assert!(matches!(result, Err(SchemaError::InvalidFrame(_))));
372 }
373
374 #[test]
375 fn test_decode_schema_invalid_chars() {
376 let invalid = format!("{}ABC{}", frame::FRAME_START, frame::FRAME_END);
377 let result = decode_schema(&invalid);
378 assert!(matches!(result, Err(SchemaError::InvalidCharacter(_))));
379 }
380
381 #[test]
382 fn test_visual_wire_format() {
383 let input = r#"{"users":[{"id":1,"name":"alice"},{"id":2,"name":"bob"}]}"#;
384 let encoded = encode_schema(input).unwrap();
385
386 println!("\n=== Visual Wire Format ===");
387 println!("Input JSON: {}", input);
388 println!("Input length: {} bytes", input.len());
389 println!("\nEncoded output: {}", encoded);
390 println!(
391 "Encoded length: {} chars ({} bytes UTF-8)",
392 encoded.chars().count(),
393 encoded.len()
394 );
395
396 let compression_ratio = input.len() as f64 / encoded.len() as f64;
398 println!("Compression ratio: {:.2}x", compression_ratio);
399
400 let decoded = decode_schema(&encoded).unwrap();
402 let input_value: serde_json::Value = serde_json::from_str(input).unwrap();
403 let output_value: serde_json::Value = serde_json::from_str(&decoded).unwrap();
404 assert_eq!(input_value, output_value);
405 println!("Roundtrip verified ✓\n");
406 }
407
408 #[test]
409 fn test_compression_comparison() {
410 let test_cases = [
411 r#"{"id":1}"#,
412 r#"{"id":1,"name":"alice"}"#,
413 r#"{"users":[{"id":1,"name":"alice"},{"id":2,"name":"bob"}]}"#,
414 r#"{"data":[1,2,3,4,5,6,7,8,9,10]}"#,
415 ];
416
417 println!("\n=== Compression Comparison ===");
418 for (i, input) in test_cases.iter().enumerate() {
419 let encoded = encode_schema(input).unwrap();
420 let ratio = input.len() as f64 / encoded.len() as f64;
421
422 println!(
423 "Test case {}: {} bytes → {} bytes ({:.2}x)",
424 i + 1,
425 input.len(),
426 encoded.len(),
427 ratio
428 );
429 }
430 println!();
431 }
432}