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