1use crate::Result;
34use crate::format::FormatKind;
35use std::borrow::Cow;
36use std::fmt;
37
38#[derive(Debug, Clone, PartialEq)]
48pub enum TapeValue<'a> {
49 Null,
51 Bool(bool),
53 Int(i64),
55 Float(f64),
57 String(Cow<'a, str>),
59 RawNumber(Cow<'a, str>),
61}
62
63impl TapeValue<'_> {
64 #[must_use]
66 pub const fn is_null(&self) -> bool {
67 matches!(self, Self::Null)
68 }
69
70 #[must_use]
72 pub const fn is_bool(&self) -> bool {
73 matches!(self, Self::Bool(_))
74 }
75
76 #[must_use]
78 pub const fn is_number(&self) -> bool {
79 matches!(self, Self::Int(_) | Self::Float(_) | Self::RawNumber(_))
80 }
81
82 #[must_use]
84 pub const fn is_string(&self) -> bool {
85 matches!(self, Self::String(_))
86 }
87
88 #[must_use]
90 pub const fn as_bool(&self) -> Option<bool> {
91 match self {
92 Self::Bool(b) => Some(*b),
93 _ => None,
94 }
95 }
96
97 #[must_use]
99 pub const fn as_int(&self) -> Option<i64> {
100 match self {
101 Self::Int(n) => Some(*n),
102 _ => None,
103 }
104 }
105
106 #[must_use]
108 pub const fn as_float(&self) -> Option<f64> {
109 match self {
110 Self::Float(n) => Some(*n),
111 _ => None,
112 }
113 }
114
115 #[must_use]
117 pub fn as_str(&self) -> Option<&str> {
118 match self {
119 Self::String(s) => Some(s),
120 _ => None,
121 }
122 }
123
124 #[must_use]
126 pub fn into_owned(self) -> TapeValue<'static> {
127 match self {
128 Self::Null => TapeValue::Null,
129 Self::Bool(b) => TapeValue::Bool(b),
130 Self::Int(n) => TapeValue::Int(n),
131 Self::Float(n) => TapeValue::Float(n),
132 Self::String(s) => TapeValue::String(Cow::Owned(s.into_owned())),
133 Self::RawNumber(s) => TapeValue::RawNumber(Cow::Owned(s.into_owned())),
134 }
135 }
136
137 #[must_use]
139 pub fn format_for_output(&self) -> String {
140 match self {
141 Self::Null => "null".to_string(),
142 Self::Bool(true) => "true".to_string(),
143 Self::Bool(false) => "false".to_string(),
144 Self::Int(n) => n.to_string(),
145 Self::Float(n) => {
146 if n.is_nan() || n.is_infinite() {
148 "null".to_string()
149 } else {
150 n.to_string()
151 }
152 }
153 Self::String(s) => format!("\"{}\"", escape_json_string(s)),
154 Self::RawNumber(s) => s.to_string(),
155 }
156 }
157}
158
159impl fmt::Display for TapeValue<'_> {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161 write!(f, "{}", self.format_for_output())
162 }
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
173pub enum TapeNodeKind {
174 ObjectStart {
176 count: usize,
178 },
179 ObjectEnd,
181 ArrayStart {
183 count: usize,
185 },
186 ArrayEnd,
188 Key,
190 Value,
192}
193
194impl TapeNodeKind {
195 #[must_use]
197 pub const fn is_container_start(&self) -> bool {
198 matches!(self, Self::ObjectStart { .. } | Self::ArrayStart { .. })
199 }
200
201 #[must_use]
203 pub const fn is_container_end(&self) -> bool {
204 matches!(self, Self::ObjectEnd | Self::ArrayEnd)
205 }
206
207 #[must_use]
209 pub const fn is_object_related(&self) -> bool {
210 matches!(self, Self::ObjectStart { .. } | Self::ObjectEnd | Self::Key)
211 }
212
213 #[must_use]
215 pub const fn is_array_related(&self) -> bool {
216 matches!(self, Self::ArrayStart { .. } | Self::ArrayEnd)
217 }
218
219 #[must_use]
221 pub const fn element_count(&self) -> Option<usize> {
222 match self {
223 Self::ObjectStart { count } | Self::ArrayStart { count } => Some(*count),
224 _ => None,
225 }
226 }
227}
228
229#[derive(Debug, Clone)]
238pub struct TapeNodeRef<'a> {
239 pub kind: TapeNodeKind,
241 pub value: Option<TapeValue<'a>>,
243 pub format: FormatKind,
245}
246
247impl<'a> TapeNodeRef<'a> {
248 #[must_use]
250 pub const fn new(kind: TapeNodeKind, value: Option<TapeValue<'a>>, format: FormatKind) -> Self {
251 Self {
252 kind,
253 value,
254 format,
255 }
256 }
257
258 #[must_use]
260 pub const fn object_start(count: usize, format: FormatKind) -> Self {
261 Self {
262 kind: TapeNodeKind::ObjectStart { count },
263 value: None,
264 format,
265 }
266 }
267
268 #[must_use]
270 pub const fn object_end(format: FormatKind) -> Self {
271 Self {
272 kind: TapeNodeKind::ObjectEnd,
273 value: None,
274 format,
275 }
276 }
277
278 #[must_use]
280 pub const fn array_start(count: usize, format: FormatKind) -> Self {
281 Self {
282 kind: TapeNodeKind::ArrayStart { count },
283 value: None,
284 format,
285 }
286 }
287
288 #[must_use]
290 pub const fn array_end(format: FormatKind) -> Self {
291 Self {
292 kind: TapeNodeKind::ArrayEnd,
293 value: None,
294 format,
295 }
296 }
297
298 #[must_use]
300 pub const fn key(name: Cow<'a, str>, format: FormatKind) -> Self {
301 Self {
302 kind: TapeNodeKind::Key,
303 value: Some(TapeValue::String(name)),
304 format,
305 }
306 }
307
308 #[must_use]
310 pub const fn value(val: TapeValue<'a>, format: FormatKind) -> Self {
311 Self {
312 kind: TapeNodeKind::Value,
313 value: Some(val),
314 format,
315 }
316 }
317}
318
319pub trait TapeSource {
349 fn format(&self) -> FormatKind;
351
352 fn len(&self) -> usize;
354
355 fn is_empty(&self) -> bool {
357 self.len() == 0
358 }
359
360 fn node_at(&self, index: usize) -> Option<TapeNodeRef<'_>>;
364
365 fn value_at(&self, index: usize) -> Option<TapeValue<'_>> {
369 self.node_at(index).and_then(|n| {
370 if matches!(n.kind, TapeNodeKind::Value) {
371 n.value
372 } else {
373 None
374 }
375 })
376 }
377
378 fn key_at(&self, index: usize) -> Option<Cow<'_, str>> {
382 self.node_at(index).and_then(|n| {
383 if matches!(n.kind, TapeNodeKind::Key) {
384 n.value.and_then(|v| {
385 if let TapeValue::String(s) = v {
386 Some(s)
387 } else {
388 None
389 }
390 })
391 } else {
392 None
393 }
394 })
395 }
396
397 fn skip_value(&self, start_index: usize) -> Result<usize>;
406
407 fn resolve_path(&self, path: &str) -> Result<Option<usize>>;
416
417 fn iter(&self) -> TapeIterator<'_, Self>
419 where
420 Self: Sized,
421 {
422 TapeIterator::new(self)
423 }
424
425 fn path_iter(&self) -> PathIterator<'_, Self>
429 where
430 Self: Sized,
431 {
432 PathIterator::new(self)
433 }
434}
435
436pub struct TapeIterator<'a, T: TapeSource> {
442 tape: &'a T,
443 index: usize,
444}
445
446impl<'a, T: TapeSource> TapeIterator<'a, T> {
447 #[must_use]
449 pub const fn new(tape: &'a T) -> Self {
450 Self { tape, index: 0 }
451 }
452}
453
454impl<'a, T: TapeSource> Iterator for TapeIterator<'a, T> {
455 type Item = TapeNodeRef<'a>;
456
457 fn next(&mut self) -> Option<Self::Item> {
458 if self.index >= self.tape.len() {
459 return None;
460 }
461 let node = self.tape.node_at(self.index);
462 self.index += 1;
463 node
464 }
465
466 fn size_hint(&self) -> (usize, Option<usize>) {
467 let remaining = self.tape.len().saturating_sub(self.index);
468 (remaining, Some(remaining))
469 }
470}
471
472impl<T: TapeSource> ExactSizeIterator for TapeIterator<'_, T> {}
473
474#[derive(Debug, Clone)]
476pub enum PathComponent {
477 Field(String),
479 Index(usize),
481}
482
483impl PathComponent {
484 #[must_use]
486 pub fn as_path_segment(&self) -> String {
487 match self {
488 Self::Field(name) => {
489 if name.chars().all(|c| c.is_alphanumeric() || c == '_') {
491 format!(".{name}")
492 } else {
493 format!("[\"{}\"]", escape_json_string(name))
494 }
495 }
496 Self::Index(i) => format!("[{i}]"),
497 }
498 }
499}
500
501struct PathState {
503 components: Vec<PathComponent>,
504 array_indices: Vec<usize>,
505}
506
507impl PathState {
508 fn new() -> Self {
509 Self {
510 components: Vec::with_capacity(16),
511 array_indices: Vec::with_capacity(8),
512 }
513 }
514
515 fn current_path(&self, prefix: &str) -> String {
516 let mut path = prefix.to_string();
517 for component in &self.components {
518 path.push_str(&component.as_path_segment());
519 }
520 path
521 }
522
523 fn push_field(&mut self, name: &str) {
524 self.components.push(PathComponent::Field(name.to_string()));
525 }
526
527 fn push_index(&mut self, index: usize) {
528 self.components.push(PathComponent::Index(index));
529 }
530
531 fn pop(&mut self) {
532 self.components.pop();
533 }
534
535 fn enter_array(&mut self) {
536 self.array_indices.push(0);
537 }
538
539 fn exit_array(&mut self) {
540 self.array_indices.pop();
541 }
542
543 fn next_array_index(&mut self) -> usize {
544 let idx = self.array_indices.last().copied().unwrap_or(0);
545 if let Some(last) = self.array_indices.last_mut() {
546 *last += 1;
547 }
548 idx
549 }
550}
551
552pub struct PathIterator<'a, T: TapeSource> {
554 tape: &'a T,
555 index: usize,
556 state: PathState,
557 prefix: String,
558}
559
560impl<'a, T: TapeSource> PathIterator<'a, T> {
561 #[must_use]
563 pub fn new(tape: &'a T) -> Self {
564 Self::with_prefix(tape, "json")
565 }
566
567 #[must_use]
569 pub fn with_prefix(tape: &'a T, prefix: &str) -> Self {
570 Self {
571 tape,
572 index: 0,
573 state: PathState::new(),
574 prefix: prefix.to_string(),
575 }
576 }
577
578 #[must_use]
580 pub fn current_path(&self) -> String {
581 self.state.current_path(&self.prefix)
582 }
583}
584
585impl<'a, T: TapeSource> Iterator for PathIterator<'a, T> {
586 type Item = (String, TapeNodeRef<'a>);
587
588 fn next(&mut self) -> Option<Self::Item> {
589 if self.index >= self.tape.len() {
590 return None;
591 }
592
593 let node = self.tape.node_at(self.index)?;
594 let path = self.state.current_path(&self.prefix);
595 self.index += 1;
596
597 match node.kind {
599 TapeNodeKind::ObjectStart { .. } => {
600 }
602 TapeNodeKind::ObjectEnd => {
603 self.state.pop();
604 }
605 TapeNodeKind::ArrayStart { .. } => {
606 self.state.enter_array();
607 }
608 TapeNodeKind::ArrayEnd => {
609 self.state.exit_array();
610 self.state.pop();
611 }
612 TapeNodeKind::Key => {
613 if let Some(TapeValue::String(ref name)) = node.value {
614 self.state.push_field(name);
615 }
616 }
617 TapeNodeKind::Value => {
618 if self.state.array_indices.is_empty() {
620 self.state.pop(); } else {
622 self.state.pop(); let next_idx = self.state.next_array_index();
624 self.state.push_index(next_idx);
625 }
626 }
627 }
628
629 Some((path, node))
630 }
631}
632
633#[must_use]
639pub fn escape_json_string(s: &str) -> String {
640 use std::fmt::Write;
641 let mut result = String::with_capacity(s.len());
642 for ch in s.chars() {
643 match ch {
644 '"' => result.push_str("\\\""),
645 '\\' => result.push_str("\\\\"),
646 '\n' => result.push_str("\\n"),
647 '\r' => result.push_str("\\r"),
648 '\t' => result.push_str("\\t"),
649 c if c.is_control() => {
650 let _ = write!(result, "\\u{:04x}", c as u32);
651 }
652 c => result.push(c),
653 }
654 }
655 result
656}
657
658#[must_use]
660pub fn unescape_json_string(s: &str) -> String {
661 let mut result = String::with_capacity(s.len());
662 let mut chars = s.chars();
663
664 while let Some(ch) = chars.next() {
665 if ch == '\\' {
666 match chars.next() {
667 Some('"') => result.push('"'),
668 Some('\\') | None => result.push('\\'),
669 Some('/') => result.push('/'),
670 Some('n') => result.push('\n'),
671 Some('r') => result.push('\r'),
672 Some('t') => result.push('\t'),
673 Some('b') => result.push('\u{0008}'),
674 Some('f') => result.push('\u{000C}'),
675 Some('u') => {
676 let hex: String = chars.by_ref().take(4).collect();
677 if let Ok(code) = u32::from_str_radix(&hex, 16)
678 && let Some(c) = char::from_u32(code)
679 {
680 result.push(c);
681 }
682 }
683 Some(c) => {
684 result.push('\\');
685 result.push(c);
686 }
687 }
688 } else {
689 result.push(ch);
690 }
691 }
692 result
693}
694
695#[cfg(test)]
700mod tests {
701 use super::*;
702 use crate::DsonError;
703
704 struct MockTape {
706 nodes: Vec<(TapeNodeKind, Option<TapeValue<'static>>)>,
707 format: FormatKind,
708 }
709
710 impl MockTape {
711 fn new(format: FormatKind) -> Self {
712 Self {
713 nodes: Vec::new(),
714 format,
715 }
716 }
717
718 fn push(&mut self, kind: TapeNodeKind, value: Option<TapeValue<'static>>) {
719 self.nodes.push((kind, value));
720 }
721 }
722
723 impl TapeSource for MockTape {
724 fn format(&self) -> FormatKind {
725 self.format
726 }
727
728 fn len(&self) -> usize {
729 self.nodes.len()
730 }
731
732 fn node_at(&self, index: usize) -> Option<TapeNodeRef<'_>> {
733 self.nodes.get(index).map(|(kind, value)| TapeNodeRef {
734 kind: *kind,
735 value: value.clone(),
736 format: self.format,
737 })
738 }
739
740 fn skip_value(&self, start_index: usize) -> Result<usize> {
741 let node = self
742 .node_at(start_index)
743 .ok_or_else(|| DsonError::InvalidField("Index out of bounds".to_string()))?;
744
745 match node.kind {
746 TapeNodeKind::ObjectStart { count } => {
747 let mut idx = start_index + 1;
749 for _ in 0..count {
750 idx += 1; idx = self.skip_value(idx)?; }
753 Ok(idx + 1) }
755 TapeNodeKind::ArrayStart { count } => {
756 let mut idx = start_index + 1;
758 for _ in 0..count {
759 idx = self.skip_value(idx)?;
760 }
761 Ok(idx + 1) }
763 _ => Ok(start_index + 1),
764 }
765 }
766
767 fn resolve_path(&self, _path: &str) -> Result<Option<usize>> {
768 Ok(None)
770 }
771 }
772
773 #[test]
774 fn test_tape_value_null() {
775 let val = TapeValue::Null;
776 assert!(val.is_null());
777 assert!(!val.is_bool());
778 assert_eq!(val.format_for_output(), "null");
779 }
780
781 #[test]
782 fn test_tape_value_bool() {
783 let val = TapeValue::Bool(true);
784 assert!(val.is_bool());
785 assert_eq!(val.as_bool(), Some(true));
786 assert_eq!(val.format_for_output(), "true");
787
788 let val = TapeValue::Bool(false);
789 assert_eq!(val.format_for_output(), "false");
790 }
791
792 #[test]
793 fn test_tape_value_int() {
794 let val = TapeValue::Int(42);
795 assert!(val.is_number());
796 assert_eq!(val.as_int(), Some(42));
797 assert_eq!(val.format_for_output(), "42");
798 }
799
800 #[test]
801 fn test_tape_value_float() {
802 let val = TapeValue::Float(3.15);
803 assert!(val.is_number());
804 assert_eq!(val.as_float(), Some(3.15));
805 }
806
807 #[test]
808 fn test_tape_value_string() {
809 let val = TapeValue::String(Cow::Borrowed("hello"));
810 assert!(val.is_string());
811 assert_eq!(val.as_str(), Some("hello"));
812 assert_eq!(val.format_for_output(), "\"hello\"");
813 }
814
815 #[test]
816 fn test_tape_value_string_escape() {
817 let val = TapeValue::String(Cow::Borrowed("hello\nworld"));
818 assert_eq!(val.format_for_output(), "\"hello\\nworld\"");
819 }
820
821 #[test]
822 fn test_tape_value_into_owned() {
823 let val = TapeValue::String(Cow::Borrowed("test"));
824 let owned = val.into_owned();
825 assert!(matches!(owned, TapeValue::String(Cow::Owned(_))));
826 }
827
828 #[test]
829 fn test_tape_node_kind_container_start() {
830 assert!(TapeNodeKind::ObjectStart { count: 2 }.is_container_start());
831 assert!(TapeNodeKind::ArrayStart { count: 3 }.is_container_start());
832 assert!(!TapeNodeKind::Value.is_container_start());
833 }
834
835 #[test]
836 fn test_tape_node_kind_element_count() {
837 assert_eq!(
838 TapeNodeKind::ObjectStart { count: 5 }.element_count(),
839 Some(5)
840 );
841 assert_eq!(TapeNodeKind::Value.element_count(), None);
842 }
843
844 #[test]
845 fn test_mock_tape_basic() {
846 let mut tape = MockTape::new(FormatKind::Json);
847 tape.push(TapeNodeKind::ObjectStart { count: 1 }, None);
848 tape.push(
849 TapeNodeKind::Key,
850 Some(TapeValue::String(Cow::Borrowed("name"))),
851 );
852 tape.push(
853 TapeNodeKind::Value,
854 Some(TapeValue::String(Cow::Borrowed("test"))),
855 );
856 tape.push(TapeNodeKind::ObjectEnd, None);
857
858 assert_eq!(tape.len(), 4);
859 assert!(!tape.is_empty());
860 assert_eq!(tape.format(), FormatKind::Json);
861 }
862
863 #[test]
864 fn test_tape_iterator() {
865 let mut tape = MockTape::new(FormatKind::Json);
866 tape.push(TapeNodeKind::Value, Some(TapeValue::Int(1)));
867 tape.push(TapeNodeKind::Value, Some(TapeValue::Int(2)));
868 tape.push(TapeNodeKind::Value, Some(TapeValue::Int(3)));
869
870 assert_eq!(tape.iter().count(), 3);
871 }
872
873 #[test]
874 fn test_tape_iterator_exact_size() {
875 let mut tape = MockTape::new(FormatKind::Json);
876 tape.push(TapeNodeKind::Value, Some(TapeValue::Int(1)));
877 tape.push(TapeNodeKind::Value, Some(TapeValue::Int(2)));
878
879 let iter = tape.iter();
880 assert_eq!(iter.len(), 2);
881 }
882
883 #[test]
884 fn test_skip_value_scalar() {
885 let mut tape = MockTape::new(FormatKind::Json);
886 tape.push(TapeNodeKind::Value, Some(TapeValue::Int(42)));
887
888 assert_eq!(tape.skip_value(0).unwrap(), 1);
889 }
890
891 #[test]
892 fn test_skip_value_object() {
893 let mut tape = MockTape::new(FormatKind::Json);
894 tape.push(TapeNodeKind::ObjectStart { count: 1 }, None);
895 tape.push(
896 TapeNodeKind::Key,
897 Some(TapeValue::String(Cow::Borrowed("a"))),
898 );
899 tape.push(TapeNodeKind::Value, Some(TapeValue::Int(1)));
900 tape.push(TapeNodeKind::ObjectEnd, None);
901
902 assert_eq!(tape.skip_value(0).unwrap(), 4);
903 }
904
905 #[test]
906 fn test_skip_value_array() {
907 let mut tape = MockTape::new(FormatKind::Json);
908 tape.push(TapeNodeKind::ArrayStart { count: 2 }, None);
909 tape.push(TapeNodeKind::Value, Some(TapeValue::Int(1)));
910 tape.push(TapeNodeKind::Value, Some(TapeValue::Int(2)));
911 tape.push(TapeNodeKind::ArrayEnd, None);
912
913 assert_eq!(tape.skip_value(0).unwrap(), 4);
914 }
915
916 #[test]
917 fn test_value_at() {
918 let mut tape = MockTape::new(FormatKind::Json);
919 tape.push(TapeNodeKind::Value, Some(TapeValue::Int(42)));
920
921 assert_eq!(tape.value_at(0), Some(TapeValue::Int(42)));
922 }
923
924 #[test]
925 fn test_key_at() {
926 let mut tape = MockTape::new(FormatKind::Json);
927 tape.push(
928 TapeNodeKind::Key,
929 Some(TapeValue::String(Cow::Borrowed("test"))),
930 );
931
932 assert_eq!(tape.key_at(0), Some(Cow::Borrowed("test")));
933 }
934
935 #[test]
936 fn test_escape_json_string() {
937 assert_eq!(escape_json_string("hello"), "hello");
938 assert_eq!(escape_json_string("hello\nworld"), "hello\\nworld");
939 assert_eq!(escape_json_string("quote\"here"), "quote\\\"here");
940 assert_eq!(escape_json_string("back\\slash"), "back\\\\slash");
941 }
942
943 #[test]
944 fn test_unescape_json_string() {
945 assert_eq!(unescape_json_string("hello"), "hello");
946 assert_eq!(unescape_json_string("hello\\nworld"), "hello\nworld");
947 assert_eq!(unescape_json_string("quote\\\"here"), "quote\"here");
948 assert_eq!(unescape_json_string("back\\\\slash"), "back\\slash");
949 }
950
951 #[test]
952 fn test_escape_unescape_roundtrip() {
953 let original = "Hello\n\"World\"\t\\Test";
954 let escaped = escape_json_string(original);
955 let unescaped = unescape_json_string(&escaped);
956 assert_eq!(unescaped, original);
957 }
958
959 #[test]
960 fn test_path_component_field() {
961 let comp = PathComponent::Field("name".to_string());
962 assert_eq!(comp.as_path_segment(), ".name");
963
964 let comp = PathComponent::Field("with space".to_string());
965 assert_eq!(comp.as_path_segment(), "[\"with space\"]");
966 }
967
968 #[test]
969 fn test_path_component_index() {
970 let comp = PathComponent::Index(42);
971 assert_eq!(comp.as_path_segment(), "[42]");
972 }
973
974 #[test]
975 fn test_tape_node_ref_constructors() {
976 let node = TapeNodeRef::object_start(5, FormatKind::Json);
977 assert!(matches!(node.kind, TapeNodeKind::ObjectStart { count: 5 }));
978
979 let node = TapeNodeRef::array_start(3, FormatKind::Json);
980 assert!(matches!(node.kind, TapeNodeKind::ArrayStart { count: 3 }));
981
982 let node = TapeNodeRef::value(TapeValue::Null, FormatKind::Json);
983 assert!(matches!(node.kind, TapeNodeKind::Value));
984 assert_eq!(node.value, Some(TapeValue::Null));
985 }
986}