1use hedl_core::Value;
55use std::collections::BTreeMap;
56
57#[derive(Debug, Clone)]
105pub struct HeaderInfo {
106 pub version: (u32, u32),
108 pub structs: BTreeMap<String, Vec<String>>,
110 pub aliases: BTreeMap<String, String>,
112 pub nests: BTreeMap<String, String>,
114}
115
116impl HeaderInfo {
117 #[must_use]
119 pub fn new() -> Self {
120 Self {
121 version: (1, 0),
122 structs: BTreeMap::new(),
123 aliases: BTreeMap::new(),
124 nests: BTreeMap::new(),
125 }
126 }
127
128 #[inline]
130 #[must_use]
131 pub fn get_schema(&self, type_name: &str) -> Option<&Vec<String>> {
132 self.structs.get(type_name)
133 }
134
135 #[inline]
137 #[must_use]
138 pub fn get_child_type(&self, parent_type: &str) -> Option<&String> {
139 self.nests.get(parent_type)
140 }
141}
142
143impl Default for HeaderInfo {
144 fn default() -> Self {
145 Self::new()
146 }
147}
148
149#[derive(Debug, Clone)]
226pub struct NodeInfo {
227 pub type_name: String,
229 pub id: String,
231 pub fields: Vec<Value>,
233 pub depth: usize,
235 pub parent_id: Option<String>,
237 pub parent_type: Option<String>,
239 pub line: usize,
241 pub child_count: Option<usize>,
243}
244
245impl NodeInfo {
246 #[must_use]
248 pub fn new(
249 type_name: String,
250 id: String,
251 fields: Vec<Value>,
252 depth: usize,
253 line: usize,
254 ) -> Self {
255 Self {
256 type_name,
257 id,
258 fields,
259 depth,
260 parent_id: None,
261 parent_type: None,
262 line,
263 child_count: None,
264 }
265 }
266
267 #[must_use]
269 pub fn with_parent(mut self, parent_type: String, parent_id: String) -> Self {
270 self.parent_type = Some(parent_type);
271 self.parent_id = Some(parent_id);
272 self
273 }
274
275 #[must_use]
277 pub fn with_child_count(mut self, count: usize) -> Self {
278 self.child_count = Some(count);
279 self
280 }
281
282 #[inline]
284 #[must_use]
285 pub fn get_field(&self, index: usize) -> Option<&Value> {
286 self.fields.get(index)
287 }
288
289 #[inline]
291 #[must_use]
292 pub fn is_nested(&self) -> bool {
293 self.depth > 0 || self.parent_id.is_some()
294 }
295}
296
297#[derive(Debug, Clone)]
299pub enum NodeEvent {
300 Header(HeaderInfo),
302
303 ListStart {
305 key: String,
307 type_name: String,
309 schema: Vec<String>,
311 line: usize,
313 },
314
315 Node(NodeInfo),
317
318 ListEnd {
320 key: String,
322 type_name: String,
324 count: usize,
326 },
327
328 Scalar {
330 key: String,
332 value: Value,
334 line: usize,
336 },
337
338 ObjectStart {
340 key: String,
342 line: usize,
344 },
345
346 ObjectEnd {
348 key: String,
350 },
351
352 EndOfDocument,
354}
355
356impl NodeEvent {
357 #[inline]
359 #[must_use]
360 pub fn is_node(&self) -> bool {
361 matches!(self, Self::Node(_))
362 }
363
364 #[inline]
366 #[must_use]
367 pub fn as_node(&self) -> Option<&NodeInfo> {
368 match self {
369 Self::Node(info) => Some(info),
370 _ => None,
371 }
372 }
373
374 #[inline]
376 #[must_use]
377 pub fn line(&self) -> Option<usize> {
378 match self {
379 Self::Header(_) => Some(1),
380 Self::ListStart { line, .. } => Some(*line),
381 Self::Node(info) => Some(info.line),
382 Self::Scalar { line, .. } => Some(*line),
383 Self::ObjectStart { line, .. } => Some(*line),
384 _ => None,
385 }
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
396 fn test_header_info_new() {
397 let header = HeaderInfo::new();
398 assert_eq!(header.version, (1, 0));
399 assert!(header.structs.is_empty());
400 assert!(header.aliases.is_empty());
401 assert!(header.nests.is_empty());
402 }
403
404 #[test]
405 fn test_header_info_default() {
406 let header = HeaderInfo::default();
407 assert_eq!(header.version, (1, 0));
408 assert!(header.structs.is_empty());
409 }
410
411 #[test]
412 fn test_header_info_get_schema() {
413 let mut header = HeaderInfo::new();
414 header.structs.insert(
415 "User".to_string(),
416 vec!["id".to_string(), "name".to_string()],
417 );
418
419 assert_eq!(
420 header.get_schema("User"),
421 Some(&vec!["id".to_string(), "name".to_string()])
422 );
423 assert_eq!(header.get_schema("Unknown"), None);
424 }
425
426 #[test]
427 fn test_header_info_get_child_type() {
428 let mut header = HeaderInfo::new();
429 header.nests.insert("User".to_string(), "Order".to_string());
430
431 assert_eq!(header.get_child_type("User"), Some(&"Order".to_string()));
432 assert_eq!(header.get_child_type("Product"), None);
433 }
434
435 #[test]
436 fn test_header_info_multiple_structs() {
437 let mut header = HeaderInfo::new();
438 header
439 .structs
440 .insert("User".to_string(), vec!["id".to_string()]);
441 header.structs.insert(
442 "Product".to_string(),
443 vec!["id".to_string(), "price".to_string()],
444 );
445 header.structs.insert(
446 "Order".to_string(),
447 vec!["id".to_string(), "user".to_string(), "product".to_string()],
448 );
449
450 assert_eq!(header.structs.len(), 3);
451 assert_eq!(header.get_schema("User").unwrap().len(), 1);
452 assert_eq!(header.get_schema("Product").unwrap().len(), 2);
453 assert_eq!(header.get_schema("Order").unwrap().len(), 3);
454 }
455
456 #[test]
457 fn test_header_info_multiple_aliases() {
458 let mut header = HeaderInfo::new();
459 header
460 .aliases
461 .insert("active".to_string(), "Active".to_string());
462 header
463 .aliases
464 .insert("inactive".to_string(), "Inactive".to_string());
465
466 assert_eq!(header.aliases.len(), 2);
467 assert_eq!(header.aliases.get("active"), Some(&"Active".to_string()));
468 }
469
470 #[test]
471 fn test_header_info_multiple_nests() {
472 let mut header = HeaderInfo::new();
473 header.nests.insert("User".to_string(), "Order".to_string());
474 header
475 .nests
476 .insert("Order".to_string(), "LineItem".to_string());
477
478 assert_eq!(header.nests.len(), 2);
479 assert_eq!(header.get_child_type("User"), Some(&"Order".to_string()));
480 assert_eq!(
481 header.get_child_type("Order"),
482 Some(&"LineItem".to_string())
483 );
484 }
485
486 #[test]
487 fn test_header_info_clone() {
488 let mut header = HeaderInfo::new();
489 header.version = (2, 1);
490 header
491 .structs
492 .insert("Test".to_string(), vec!["col".to_string()]);
493
494 let cloned = header.clone();
495 assert_eq!(cloned.version, (2, 1));
496 assert_eq!(cloned.structs.get("Test"), Some(&vec!["col".to_string()]));
497 }
498
499 #[test]
500 fn test_header_info_debug() {
501 let header = HeaderInfo::new();
502 let debug = format!("{header:?}");
503 assert!(debug.contains("HeaderInfo"));
504 assert!(debug.contains("version"));
505 }
506
507 #[test]
510 fn test_node_info_new() {
511 let node = NodeInfo::new(
512 "User".to_string(),
513 "alice".to_string(),
514 vec![
515 Value::String("alice".to_string().into()),
516 Value::String("Alice".to_string().into()),
517 ],
518 0,
519 10,
520 );
521
522 assert_eq!(node.type_name, "User");
523 assert_eq!(node.id, "alice");
524 assert_eq!(node.fields.len(), 2);
525 assert_eq!(node.depth, 0);
526 assert_eq!(node.line, 10);
527 assert_eq!(node.parent_id, None);
528 assert_eq!(node.parent_type, None);
529 }
530
531 #[test]
532 fn test_node_info_with_parent() {
533 let node = NodeInfo::new(
534 "Order".to_string(),
535 "order1".to_string(),
536 vec![Value::String("order1".to_string().into())],
537 1,
538 15,
539 )
540 .with_parent("User".to_string(), "alice".to_string());
541
542 assert_eq!(node.parent_type, Some("User".to_string()));
543 assert_eq!(node.parent_id, Some("alice".to_string()));
544 }
545
546 #[test]
547 fn test_node_info_get_field() {
548 let node = NodeInfo::new(
549 "Data".to_string(),
550 "row1".to_string(),
551 vec![
552 Value::String("row1".to_string().into()),
553 Value::Int(42),
554 Value::Bool(true),
555 ],
556 0,
557 5,
558 );
559
560 assert_eq!(
561 node.get_field(0),
562 Some(&Value::String("row1".to_string().into()))
563 );
564 assert_eq!(node.get_field(1), Some(&Value::Int(42)));
565 assert_eq!(node.get_field(2), Some(&Value::Bool(true)));
566 assert_eq!(node.get_field(3), None);
567 assert_eq!(node.get_field(100), None);
568 }
569
570 #[test]
571 fn test_node_info_is_nested_by_depth() {
572 let nested = NodeInfo::new("Child".to_string(), "c1".to_string(), vec![], 1, 10);
573 assert!(nested.is_nested());
574
575 let top_level = NodeInfo::new("Parent".to_string(), "p1".to_string(), vec![], 0, 5);
576 assert!(!top_level.is_nested());
577 }
578
579 #[test]
580 fn test_node_info_is_nested_by_parent() {
581 let with_parent = NodeInfo::new("Child".to_string(), "c1".to_string(), vec![], 0, 10)
582 .with_parent("Parent".to_string(), "p1".to_string());
583 assert!(with_parent.is_nested());
584 }
585
586 #[test]
587 fn test_node_info_clone() {
588 let node = NodeInfo::new(
589 "User".to_string(),
590 "alice".to_string(),
591 vec![Value::String("alice".to_string().into())],
592 0,
593 1,
594 );
595 let cloned = node.clone();
596
597 assert_eq!(cloned.type_name, "User");
598 assert_eq!(cloned.id, "alice");
599 }
600
601 #[test]
602 fn test_node_info_debug() {
603 let node = NodeInfo::new("User".to_string(), "alice".to_string(), vec![], 0, 1);
604 let debug = format!("{node:?}");
605 assert!(debug.contains("NodeInfo"));
606 assert!(debug.contains("User"));
607 assert!(debug.contains("alice"));
608 }
609
610 #[test]
611 fn test_node_info_empty_fields() {
612 let node = NodeInfo::new("Empty".to_string(), "e1".to_string(), vec![], 0, 1);
613 assert!(node.fields.is_empty());
614 assert_eq!(node.get_field(0), None);
615 }
616
617 #[test]
618 fn test_node_info_all_value_types() {
619 let node = NodeInfo::new(
620 "AllTypes".to_string(),
621 "test".to_string(),
622 vec![
623 Value::Null,
624 Value::Bool(true),
625 Value::Int(-42),
626 Value::Float(3.5),
627 Value::String("hello".to_string().into()),
628 ],
629 0,
630 1,
631 );
632
633 assert_eq!(node.get_field(0), Some(&Value::Null));
634 assert_eq!(node.get_field(1), Some(&Value::Bool(true)));
635 assert_eq!(node.get_field(2), Some(&Value::Int(-42)));
636 assert_eq!(node.get_field(3), Some(&Value::Float(3.5)));
637 assert_eq!(
638 node.get_field(4),
639 Some(&Value::String("hello".to_string().into()))
640 );
641 }
642
643 #[test]
646 fn test_node_event_is_node() {
647 let node_info = NodeInfo::new("User".to_string(), "a".to_string(), vec![], 0, 1);
648 let node_event = NodeEvent::Node(node_info);
649 assert!(node_event.is_node());
650
651 let header_event = NodeEvent::Header(HeaderInfo::new());
652 assert!(!header_event.is_node());
653
654 let list_start = NodeEvent::ListStart {
655 key: "users".to_string(),
656 type_name: "User".to_string(),
657 schema: vec![],
658 line: 1,
659 };
660 assert!(!list_start.is_node());
661 }
662
663 #[test]
664 fn test_node_event_as_node() {
665 let node_info = NodeInfo::new("User".to_string(), "alice".to_string(), vec![], 0, 5);
666 let node_event = NodeEvent::Node(node_info);
667
668 let extracted = node_event.as_node().unwrap();
669 assert_eq!(extracted.id, "alice");
670
671 let header_event = NodeEvent::Header(HeaderInfo::new());
672 assert!(header_event.as_node().is_none());
673 }
674
675 #[test]
676 fn test_node_event_line_header() {
677 let event = NodeEvent::Header(HeaderInfo::new());
678 assert_eq!(event.line(), Some(1));
679 }
680
681 #[test]
682 fn test_node_event_line_list_start() {
683 let event = NodeEvent::ListStart {
684 key: "users".to_string(),
685 type_name: "User".to_string(),
686 schema: vec![],
687 line: 42,
688 };
689 assert_eq!(event.line(), Some(42));
690 }
691
692 #[test]
693 fn test_node_event_line_node() {
694 let node = NodeInfo::new("User".to_string(), "a".to_string(), vec![], 0, 100);
695 let event = NodeEvent::Node(node);
696 assert_eq!(event.line(), Some(100));
697 }
698
699 #[test]
700 fn test_node_event_line_scalar() {
701 let event = NodeEvent::Scalar {
702 key: "name".to_string(),
703 value: Value::String("test".to_string().into()),
704 line: 25,
705 };
706 assert_eq!(event.line(), Some(25));
707 }
708
709 #[test]
710 fn test_node_event_line_object_start() {
711 let event = NodeEvent::ObjectStart {
712 key: "config".to_string(),
713 line: 50,
714 };
715 assert_eq!(event.line(), Some(50));
716 }
717
718 #[test]
719 fn test_node_event_line_list_end() {
720 let event = NodeEvent::ListEnd {
721 key: "users".to_string(),
722 type_name: "User".to_string(),
723 count: 10,
724 };
725 assert_eq!(event.line(), None);
726 }
727
728 #[test]
729 fn test_node_event_line_object_end() {
730 let event = NodeEvent::ObjectEnd {
731 key: "config".to_string(),
732 };
733 assert_eq!(event.line(), None);
734 }
735
736 #[test]
737 fn test_node_event_line_end_of_document() {
738 let event = NodeEvent::EndOfDocument;
739 assert_eq!(event.line(), None);
740 }
741
742 #[test]
743 fn test_node_event_clone() {
744 let event = NodeEvent::Scalar {
745 key: "key".to_string(),
746 value: Value::Int(42),
747 line: 10,
748 };
749 let cloned = event.clone();
750
751 if let NodeEvent::Scalar { key, value, line } = cloned {
752 assert_eq!(key, "key");
753 assert_eq!(value, Value::Int(42));
754 assert_eq!(line, 10);
755 } else {
756 panic!("Expected Scalar");
757 }
758 }
759
760 #[test]
761 fn test_node_event_debug() {
762 let event = NodeEvent::EndOfDocument;
763 let debug = format!("{event:?}");
764 assert!(debug.contains("EndOfDocument"));
765 }
766
767 #[test]
768 fn test_node_event_list_start_fields() {
769 let event = NodeEvent::ListStart {
770 key: "users".to_string(),
771 type_name: "User".to_string(),
772 schema: vec!["id".to_string(), "name".to_string()],
773 line: 5,
774 };
775
776 if let NodeEvent::ListStart {
777 key,
778 type_name,
779 schema,
780 line,
781 } = event
782 {
783 assert_eq!(key, "users");
784 assert_eq!(type_name, "User");
785 assert_eq!(schema, vec!["id".to_string(), "name".to_string()]);
786 assert_eq!(line, 5);
787 }
788 }
789
790 #[test]
791 fn test_node_event_list_end_fields() {
792 let event = NodeEvent::ListEnd {
793 key: "products".to_string(),
794 type_name: "Product".to_string(),
795 count: 100,
796 };
797
798 if let NodeEvent::ListEnd {
799 key,
800 type_name,
801 count,
802 } = event
803 {
804 assert_eq!(key, "products");
805 assert_eq!(type_name, "Product");
806 assert_eq!(count, 100);
807 }
808 }
809
810 #[test]
811 fn test_node_event_scalar_all_value_types() {
812 let events = [
813 NodeEvent::Scalar {
814 key: "null".to_string(),
815 value: Value::Null,
816 line: 1,
817 },
818 NodeEvent::Scalar {
819 key: "bool".to_string(),
820 value: Value::Bool(true),
821 line: 2,
822 },
823 NodeEvent::Scalar {
824 key: "int".to_string(),
825 value: Value::Int(-100),
826 line: 3,
827 },
828 NodeEvent::Scalar {
829 key: "float".to_string(),
830 value: Value::Float(2.75),
831 line: 4,
832 },
833 NodeEvent::Scalar {
834 key: "string".to_string(),
835 value: Value::String("text".to_string().into()),
836 line: 5,
837 },
838 ];
839
840 for (i, event) in events.iter().enumerate() {
841 assert_eq!(event.line(), Some(i + 1));
842 assert!(!event.is_node());
843 }
844 }
845}