fionn_ops/processor/
tape_ops.rs1use crate::{DsonOperation, OperationValue};
9use fionn_tape::DsonTape;
10use simd_json::value::tape::Node;
11use std::collections::HashMap;
12
13pub struct TapeDsonProcessor {
15 operations: Vec<DsonOperation>,
17 field_values: HashMap<String, OperationValue>,
19}
20
21impl TapeDsonProcessor {
22 #[must_use]
24 pub fn new() -> Self {
25 Self {
26 operations: Vec::new(),
27 field_values: HashMap::new(),
28 }
29 }
30
31 pub fn extract_operations(&mut self, tape: &DsonTape) -> Result<&[DsonOperation], String> {
36 self.operations.clear();
37 self.field_values.clear();
38
39 let nodes = tape.nodes();
40 if nodes.is_empty() {
41 return Ok(&self.operations);
42 }
43
44 self.process_node(nodes, 0, &mut Vec::new())?;
46
47 Ok(&self.operations)
48 }
49
50 fn process_node(
52 &mut self,
53 nodes: &[Node<'static>],
54 index: usize,
55 path_stack: &mut Vec<String>,
56 ) -> Result<usize, String> {
57 if index >= nodes.len() {
58 return Ok(index);
59 }
60
61 let node = &nodes[index];
62 let current_path = path_stack.join(".");
63
64 match node {
65 Node::String(s) => {
66 if !current_path.is_empty() {
67 let value = OperationValue::StringRef(s.to_string());
68 self.field_values
69 .insert(current_path.clone(), value.clone());
70 self.operations.push(DsonOperation::FieldAdd {
71 path: current_path,
72 value,
73 });
74 }
75 Ok(index + 1)
76 }
77 Node::Static(static_val) => {
78 if !current_path.is_empty() {
79 let value = match static_val {
80 simd_json::StaticNode::Null => OperationValue::Null,
81 simd_json::StaticNode::Bool(b) => OperationValue::BoolRef(*b),
82 simd_json::StaticNode::I64(n) => OperationValue::NumberRef(n.to_string()),
83 simd_json::StaticNode::U64(n) => OperationValue::NumberRef(n.to_string()),
84 simd_json::StaticNode::F64(n) => OperationValue::NumberRef(n.to_string()),
85 };
86 self.field_values
87 .insert(current_path.clone(), value.clone());
88 self.operations.push(DsonOperation::FieldAdd {
89 path: current_path,
90 value,
91 });
92 }
93 Ok(index + 1)
94 }
95 Node::Object { len, count: _ } => {
96 self.operations.push(DsonOperation::ObjectStart {
97 path: current_path.clone(),
98 });
99
100 let mut current_idx = index + 1;
101
102 for _ in 0..*len {
104 if current_idx >= nodes.len() {
105 break;
106 }
107
108 if let Node::String(field_name) = &nodes[current_idx] {
110 let field_name_str = field_name.to_string();
111 path_stack.push(field_name_str);
112 current_idx += 1;
113
114 if current_idx < nodes.len() {
116 current_idx = self.process_node(nodes, current_idx, path_stack)?;
117 }
118
119 path_stack.pop();
120 } else {
121 current_idx += 1;
122 }
123 }
124
125 self.operations
126 .push(DsonOperation::ObjectEnd { path: current_path });
127
128 Ok(current_idx)
129 }
130 Node::Array { len, count: _ } => {
131 self.operations.push(DsonOperation::ArrayStart {
132 path: current_path.clone(),
133 });
134
135 let mut current_idx = index + 1;
136
137 for elem_idx in 0..*len {
139 if current_idx >= nodes.len() {
140 break;
141 }
142
143 path_stack.push(format!("[{elem_idx}]"));
144 current_idx = self.process_node(nodes, current_idx, path_stack)?;
145 path_stack.pop();
146 }
147
148 self.operations
149 .push(DsonOperation::ArrayEnd { path: current_path });
150
151 Ok(current_idx)
152 }
153 }
154 }
155
156 #[must_use]
158 pub fn get_field(&self, path: &str) -> Option<&OperationValue> {
159 self.field_values.get(path)
160 }
161
162 #[must_use]
164 pub fn operations(&self) -> &[DsonOperation] {
165 &self.operations
166 }
167
168 #[must_use]
170 pub const fn field_values(&self) -> &HashMap<String, OperationValue> {
171 &self.field_values
172 }
173
174 pub fn serialize_to_json(&self) -> Result<String, String> {
179 let mut root = serde_json::Map::new();
180
181 for op in &self.operations {
182 match op {
183 DsonOperation::FieldAdd { path, value }
184 | DsonOperation::FieldModify { path, value } => {
185 Self::set_value_at_path(&mut root, path, value);
186 }
187 _ => {}
188 }
189 }
190
191 serde_json::to_string(&serde_json::Value::Object(root))
192 .map_err(|e| format!("Serialization error: {e}"))
193 }
194
195 fn set_value_at_path(
197 root: &mut serde_json::Map<String, serde_json::Value>,
198 path: &str,
199 value: &OperationValue,
200 ) {
201 let parts: Vec<&str> = path.split('.').collect();
202 let json_value = Self::operation_value_to_json(value);
203
204 if parts.is_empty() {
205 return;
206 }
207
208 let mut current = root;
209
210 for (i, part) in parts.iter().enumerate() {
211 if i == parts.len() - 1 {
212 if let Some(bracket_pos) = part.find('[') {
214 let field_name = &part[..bracket_pos];
215 current.insert(field_name.to_string(), json_value.clone());
217 } else {
218 current.insert(part.to_string(), json_value.clone());
219 }
220 } else {
221 if !current.contains_key(*part) {
223 current.insert(
224 part.to_string(),
225 serde_json::Value::Object(serde_json::Map::new()),
226 );
227 }
228 if let Some(serde_json::Value::Object(obj)) = current.get_mut(*part) {
229 current = obj;
230 } else {
231 return;
232 }
233 }
234 }
235 }
236
237 fn operation_value_to_json(value: &OperationValue) -> serde_json::Value {
239 match value {
240 OperationValue::StringRef(s) => serde_json::Value::String(s.clone()),
241 OperationValue::NumberRef(n) => n.parse::<i64>().map_or_else(
242 |_| {
243 n.parse::<f64>().map_or_else(
244 |_| serde_json::Value::String(n.clone()),
245 |num| {
246 serde_json::Number::from_f64(num).map_or_else(
247 || serde_json::Value::String(n.clone()),
248 serde_json::Value::Number,
249 )
250 },
251 )
252 },
253 |num| serde_json::Value::Number(num.into()),
254 ),
255 OperationValue::BoolRef(b) => serde_json::Value::Bool(*b),
256 OperationValue::Null => serde_json::Value::Null,
257 OperationValue::ObjectRef { .. } => serde_json::Value::Object(serde_json::Map::new()),
258 OperationValue::ArrayRef { .. } => serde_json::Value::Array(Vec::new()),
259 }
260 }
261}
262
263impl Default for TapeDsonProcessor {
264 fn default() -> Self {
265 Self::new()
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn test_extract_operations() {
275 let mut processor = TapeDsonProcessor::new();
276 let json = r#"{"name": "Alice", "age": 30}"#;
277
278 let tape = DsonTape::parse(json).unwrap();
279 let ops = processor.extract_operations(&tape).unwrap();
280
281 assert!(!ops.is_empty());
283 }
284
285 #[test]
286 fn test_nested_object() {
287 let mut processor = TapeDsonProcessor::new();
288 let json = r#"{"user": {"name": "Bob", "active": true}}"#;
289
290 let tape = DsonTape::parse(json).unwrap();
291 processor.extract_operations(&tape).unwrap();
292
293 assert!(processor.get_field("user.name").is_some());
295 assert!(processor.get_field("user.active").is_some());
296 }
297
298 #[test]
299 fn test_array_processing() {
300 let mut processor = TapeDsonProcessor::new();
301 let json = r#"{"items": [1, 2, 3]}"#;
302
303 let tape = DsonTape::parse(json).unwrap();
304 let ops = processor.extract_operations(&tape).unwrap();
305
306 let has_array_start = ops
308 .iter()
309 .any(|op| matches!(op, DsonOperation::ArrayStart { .. }));
310 assert!(has_array_start);
311 }
312
313 #[test]
314 fn test_serialize_to_json() {
315 let mut processor = TapeDsonProcessor::new();
316 let json = r#"{"name": "Alice", "age": 30}"#;
317
318 let tape = DsonTape::parse(json).unwrap();
319 processor.extract_operations(&tape).unwrap();
320
321 let result = processor.serialize_to_json().unwrap();
322 assert!(result.contains("Alice"));
323 assert!(result.contains("30"));
324 }
325
326 #[test]
329 fn test_default() {
330 let processor = TapeDsonProcessor::default();
331 assert!(processor.operations().is_empty());
332 assert!(processor.field_values().is_empty());
333 }
334
335 #[test]
336 fn test_empty_tape() {
337 let mut processor = TapeDsonProcessor::new();
338 let json = r"{}";
339
340 let tape = DsonTape::parse(json).unwrap();
341 let ops = processor.extract_operations(&tape).unwrap();
342
343 assert!(!ops.is_empty());
345 }
346
347 #[test]
348 fn test_operations_getter() {
349 let mut processor = TapeDsonProcessor::new();
350 let json = r#"{"x": 1}"#;
351
352 let tape = DsonTape::parse(json).unwrap();
353 processor.extract_operations(&tape).unwrap();
354
355 let ops = processor.operations();
356 assert!(!ops.is_empty());
357 }
358
359 #[test]
360 fn test_field_values_getter() {
361 let mut processor = TapeDsonProcessor::new();
362 let json = r#"{"x": 1}"#;
363
364 let tape = DsonTape::parse(json).unwrap();
365 processor.extract_operations(&tape).unwrap();
366
367 let values = processor.field_values();
368 assert!(values.contains_key("x"));
369 }
370
371 #[test]
372 fn test_get_field_nonexistent() {
373 let processor = TapeDsonProcessor::new();
374 assert!(processor.get_field("nonexistent").is_none());
375 }
376
377 #[test]
378 fn test_null_value() {
379 let mut processor = TapeDsonProcessor::new();
380 let json = r#"{"value": null}"#;
381
382 let tape = DsonTape::parse(json).unwrap();
383 processor.extract_operations(&tape).unwrap();
384
385 let value = processor.get_field("value");
386 assert!(value.is_some());
387 assert!(matches!(value.unwrap(), OperationValue::Null));
388 }
389
390 #[test]
391 fn test_bool_value() {
392 let mut processor = TapeDsonProcessor::new();
393 let json = r#"{"active": true, "deleted": false}"#;
394
395 let tape = DsonTape::parse(json).unwrap();
396 processor.extract_operations(&tape).unwrap();
397
398 let active = processor.get_field("active");
399 assert!(matches!(active.unwrap(), OperationValue::BoolRef(true)));
400
401 let deleted = processor.get_field("deleted");
402 assert!(matches!(deleted.unwrap(), OperationValue::BoolRef(false)));
403 }
404
405 #[test]
406 fn test_float_value() {
407 let mut processor = TapeDsonProcessor::new();
408 let json = r#"{"pi": 3.14159}"#;
409
410 let tape = DsonTape::parse(json).unwrap();
411 processor.extract_operations(&tape).unwrap();
412
413 let value = processor.get_field("pi");
414 assert!(value.is_some());
415 }
416
417 #[test]
418 fn test_negative_number() {
419 let mut processor = TapeDsonProcessor::new();
420 let json = r#"{"temp": -10}"#;
421
422 let tape = DsonTape::parse(json).unwrap();
423 processor.extract_operations(&tape).unwrap();
424
425 let value = processor.get_field("temp");
426 assert!(value.is_some());
427 }
428
429 #[test]
430 fn test_deeply_nested() {
431 let mut processor = TapeDsonProcessor::new();
432 let json = r#"{"a": {"b": {"c": {"d": 42}}}}"#;
433
434 let tape = DsonTape::parse(json).unwrap();
435 processor.extract_operations(&tape).unwrap();
436
437 let value = processor.get_field("a.b.c.d");
438 assert!(value.is_some());
439 }
440
441 #[test]
442 fn test_array_of_objects() {
443 let mut processor = TapeDsonProcessor::new();
444 let json = r#"{"users": [{"name": "Alice"}, {"name": "Bob"}]}"#;
445
446 let tape = DsonTape::parse(json).unwrap();
447 let ops = processor.extract_operations(&tape).unwrap();
448
449 let has_array_start = ops
450 .iter()
451 .any(|op| matches!(op, DsonOperation::ArrayStart { .. }));
452 let has_array_end = ops
453 .iter()
454 .any(|op| matches!(op, DsonOperation::ArrayEnd { .. }));
455 assert!(has_array_start);
456 assert!(has_array_end);
457 }
458
459 #[test]
460 fn test_nested_arrays() {
461 let mut processor = TapeDsonProcessor::new();
462 let json = r#"{"matrix": [[1, 2], [3, 4]]}"#;
463
464 let tape = DsonTape::parse(json).unwrap();
465 let ops = processor.extract_operations(&tape).unwrap();
466
467 assert!(!ops.is_empty());
468 }
469
470 #[test]
471 fn test_serialize_nested_object() {
472 let mut processor = TapeDsonProcessor::new();
473 let json = r#"{"user": {"name": "Alice"}}"#;
474
475 let tape = DsonTape::parse(json).unwrap();
476 processor.extract_operations(&tape).unwrap();
477
478 let result = processor.serialize_to_json().unwrap();
479 assert!(result.contains("user"));
480 assert!(result.contains("name"));
481 assert!(result.contains("Alice"));
482 }
483
484 #[test]
485 fn test_serialize_boolean() {
486 let mut processor = TapeDsonProcessor::new();
487 let json = r#"{"active": true}"#;
488
489 let tape = DsonTape::parse(json).unwrap();
490 processor.extract_operations(&tape).unwrap();
491
492 let result = processor.serialize_to_json().unwrap();
493 assert!(result.contains("true"));
494 }
495
496 #[test]
497 fn test_serialize_null() {
498 let mut processor = TapeDsonProcessor::new();
499 let json = r#"{"value": null}"#;
500
501 let tape = DsonTape::parse(json).unwrap();
502 processor.extract_operations(&tape).unwrap();
503
504 let result = processor.serialize_to_json().unwrap();
505 assert!(result.contains("null"));
506 }
507
508 #[test]
509 fn test_serialize_float() {
510 let mut processor = TapeDsonProcessor::new();
511 let json = r#"{"pi": 3.14}"#;
512
513 let tape = DsonTape::parse(json).unwrap();
514 processor.extract_operations(&tape).unwrap();
515
516 let result = processor.serialize_to_json().unwrap();
517 assert!(result.contains("3.14"));
518 }
519
520 #[test]
521 fn test_extract_operations_clears_previous() {
522 let mut processor = TapeDsonProcessor::new();
523
524 let json1 = r#"{"first": 1}"#;
526 let tape1 = DsonTape::parse(json1).unwrap();
527 processor.extract_operations(&tape1).unwrap();
528 assert!(processor.get_field("first").is_some());
529
530 let json2 = r#"{"second": 2}"#;
532 let tape2 = DsonTape::parse(json2).unwrap();
533 processor.extract_operations(&tape2).unwrap();
534
535 assert!(processor.get_field("first").is_none());
537 assert!(processor.get_field("second").is_some());
538 }
539
540 #[test]
541 fn test_operation_value_to_json_object_ref() {
542 let value = OperationValue::ObjectRef { start: 0, end: 10 };
543 let json = TapeDsonProcessor::operation_value_to_json(&value);
544 assert!(json.is_object());
545 }
546
547 #[test]
548 fn test_operation_value_to_json_array_ref() {
549 let value = OperationValue::ArrayRef { start: 0, end: 10 };
550 let json = TapeDsonProcessor::operation_value_to_json(&value);
551 assert!(json.is_array());
552 }
553
554 #[test]
555 fn test_operation_value_to_json_invalid_float() {
556 let value = OperationValue::NumberRef("NaN".to_string());
558 let json = TapeDsonProcessor::operation_value_to_json(&value);
559 assert!(json.is_string());
561 }
562
563 #[test]
564 fn test_large_integer() {
565 let mut processor = TapeDsonProcessor::new();
566 let json = r#"{"big": 9223372036854775807}"#;
567
568 let tape = DsonTape::parse(json).unwrap();
569 processor.extract_operations(&tape).unwrap();
570
571 let value = processor.get_field("big");
572 assert!(value.is_some());
573 }
574
575 #[test]
576 fn test_string_with_special_chars() {
577 let mut processor = TapeDsonProcessor::new();
578 let json = r#"{"msg": "hello\nworld"}"#;
579
580 let tape = DsonTape::parse(json).unwrap();
581 processor.extract_operations(&tape).unwrap();
582
583 let value = processor.get_field("msg");
584 assert!(value.is_some());
585 }
586
587 #[test]
588 fn test_array_with_mixed_types() {
589 let mut processor = TapeDsonProcessor::new();
590 let json = r#"{"mixed": [1, "two", true, null]}"#;
591
592 let tape = DsonTape::parse(json).unwrap();
593 let ops = processor.extract_operations(&tape).unwrap();
594
595 assert!(!ops.is_empty());
596 }
597
598 #[test]
599 fn test_serialize_with_array_notation_path() {
600 let mut processor = TapeDsonProcessor::new();
601
602 processor.operations.push(DsonOperation::FieldAdd {
604 path: "items[0]".to_string(),
605 value: OperationValue::NumberRef("1".to_string()),
606 });
607
608 let result = processor.serialize_to_json().unwrap();
609 assert!(result.contains("items"));
611 }
612}