1pub mod decode;
22mod parser;
23pub mod write;
24
25#[cfg(test)]
26pub(crate) mod test;
27
28#[cfg(test)]
29mod test_line_col;
30
31#[cfg(test)]
32mod test_path;
33
34#[cfg(test)]
35mod test_path_matches_glob;
36
37#[cfg(test)]
38mod test_source_json;
39
40use std::{
41 borrow::{Borrow, Cow},
42 collections::{btree_set, BTreeMap, BTreeSet},
43 fmt::{self, Write as _},
44 rc::Rc,
45};
46
47use crate::{
48 string,
49 warning::{Caveat, CaveatDeferred},
50};
51
52pub(crate) use parser::parse;
53pub use parser::{Error, ErrorKind as ParseErrorKind};
54
55pub fn parse_object(json: &str) -> Result<Document<'_>, ParseError> {
60 let json = string::ReasonableLen::new(json).map_err(|_e| ParseError::SizeExceedsMax)?;
61 let doc = parse(json).map_err(ParseError::Json)?;
62
63 if !doc.root().is_object() {
64 return Err(ParseError::ShouldBeAnObject);
65 }
66
67 Ok(doc)
68}
69
70#[derive(Debug)]
71pub enum ParseError {
72 Json(Error),
74
75 ShouldBeAnObject,
77
78 SizeExceedsMax,
80}
81
82impl fmt::Display for ParseError {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 match self {
85 Self::Json(error) => write!(f, "{error}"),
86 Self::ShouldBeAnObject => f.write_str("The CDR should be an object."),
87 Self::SizeExceedsMax => write!(
88 f,
89 "The input `&str` exceeds the reasonable maximum `{} MB`.",
90 string::ReasonableLen::FACTOR
91 ),
92 }
93 }
94}
95
96impl std::error::Error for ParseError {
97 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
98 match &self {
99 ParseError::Json(err) => Some(err),
100 ParseError::ShouldBeAnObject | ParseError::SizeExceedsMax => None,
101 }
102 }
103}
104
105#[derive(Clone, Debug)]
107pub struct Document<'buf> {
108 inner: Rc<DocumentInner<'buf>>,
110 root: Element<'buf>,
112}
113
114impl<'buf> Document<'buf> {
115 pub fn source(&self) -> &'buf str {
117 self.inner.source
118 }
119
120 pub fn root(&self) -> &Element<'buf> {
122 &self.root
123 }
124}
125
126#[derive(Clone, Debug)]
131pub struct Element<'buf> {
132 doc: Rc<DocumentInner<'buf>>,
134 id: ElemId,
136 span: Span,
138 full_span_end: u32,
141 value: Value<'buf>,
143}
144
145impl PartialEq for Element<'_> {
146 fn eq(&self, other: &Self) -> bool {
147 self.id == other.id
148 && self.span == other.span
149 && self.full_span_end == other.full_span_end
150 && self.value == other.value
151 }
152}
153
154impl Eq for Element<'_> {}
155
156impl<'buf> Element<'buf> {
157 pub fn id(&self) -> ElemId {
158 self.id
159 }
160
161 pub fn span(&self) -> Span {
162 self.span
163 }
164
165 pub fn full_span(&self) -> Span {
181 Span {
182 start: self.span.start,
183 end: self.full_span_end,
184 }
185 }
186
187 pub fn value(&self) -> &Value<'buf> {
188 &self.value
189 }
190
191 pub fn path(&self) -> Path {
195 self.doc.paths.path_of(self)
196 }
197
198 #[expect(
200 clippy::string_slice,
201 reason = "spans are produced by the parser from the same source, so slices are always valid"
202 )]
203 #[expect(
204 clippy::as_conversions,
205 reason = "The index is guaranteed within bounds by the parser"
206 )]
207 pub fn source_json_value(&self) -> &'buf str {
208 &self.doc.source[self.span.start as usize..self.span.end as usize]
209 }
210
211 pub fn source(&self) -> &'buf str {
213 self.doc.source
214 }
215
216 #[expect(
218 clippy::string_slice,
219 reason = "spans are produced by the parser from the same source, so slices are always valid"
220 )]
221 #[expect(
222 clippy::as_conversions,
223 reason = "The index is guaranteed within bounds by the parser"
224 )]
225 pub fn location(&self) -> Location {
226 let source = self.doc.source;
227
228 let lead_in = &source[..self.span.start as usize];
230 line_col(lead_in)
231 }
232
233 pub fn as_value(&self) -> &Value<'buf> {
235 &self.value
236 }
237
238 pub fn to_raw_str(&self) -> Option<RawStr<'buf>> {
240 self.value.to_raw_str()
241 }
242
243 pub fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
245 self.value.as_object_fields()
246 }
247
248 pub fn as_array(&self) -> Option<&[Element<'buf>]> {
249 self.value.as_array()
250 }
251
252 pub fn as_number_str(&self) -> Option<&str> {
253 self.value.as_number()
254 }
255
256 pub fn is_null(&self) -> bool {
258 self.value.is_null()
259 }
260
261 pub fn is_object(&self) -> bool {
263 self.value.is_object()
264 }
265
266 pub fn is_array(&self) -> bool {
268 self.value.is_array()
269 }
270}
271
272#[derive(Clone, Debug, Eq, PartialEq)]
274pub enum Value<'buf> {
275 Null,
277 True,
279 False,
281 String(RawStr<'buf>),
283 Number(&'buf str),
285 Array(Vec<Element<'buf>>),
287 Object(Vec<Field<'buf>>),
289}
290
291impl fmt::Display for Value<'_> {
292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293 match self {
294 Self::Null => write!(f, "null"),
295 Self::True => write!(f, "true"),
296 Self::False => write!(f, "false"),
297 Self::String(s) => write!(f, "{}", s.as_unescaped_str()),
298 Self::Number(s) => write!(f, "{s}"),
299 Self::Array(..) => f.write_str("[...]"),
300 Self::Object(..) => f.write_str("{...}"),
301 }
302 }
303}
304
305#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
307pub struct Span {
308 pub start: u32,
310 pub end: u32,
312}
313
314impl Span {
315 fn new(start: u32, end: u32) -> Self {
316 Self { start, end }
317 }
318}
319
320#[derive(Clone, Copy, Debug, PartialEq, Eq)]
322pub struct Location {
323 pub line: u32,
325
326 pub col: u32,
328}
329
330impl From<(u32, u32)> for Location {
331 fn from(value: (u32, u32)) -> Self {
332 Self {
333 line: value.0,
334 col: value.1,
335 }
336 }
337}
338
339impl From<Location> for (u32, u32) {
340 fn from(value: Location) -> Self {
341 (value.line, value.col)
342 }
343}
344
345impl PartialEq<(u32, u32)> for Location {
346 fn eq(&self, other: &(u32, u32)) -> bool {
347 self.line == other.0 && self.col == other.1
348 }
349}
350
351impl fmt::Display for Location {
352 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353 write!(f, "{}:{}", self.line, self.col)
354 }
355}
356
357pub fn line_col(s: &str) -> Location {
361 let mut chars = s.chars().rev();
362 let mut line = 0_u32;
363 let mut col = 0_u32;
364
365 for c in chars.by_ref() {
370 if c == '\n' {
372 let Some(n) = line.checked_add(1) else {
373 break;
374 };
375 line = n;
376 break;
377 }
378 let Some(n) = col.checked_add(1) else {
379 break;
380 };
381 col = n;
382 }
383
384 for c in chars {
386 if c == '\n' {
387 let Some(n) = line.checked_add(1) else {
388 break;
389 };
390 line = n;
391 }
392 }
393
394 Location { line, col }
395}
396
397#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
402pub struct ElemId(usize);
403
404#[derive(Debug)]
406enum PathEntry<'buf> {
407 Root,
409 Field {
411 parent: ElemId,
413 key: RawStr<'buf>,
415 },
416 Item {
418 parent: ElemId,
420 index: u32,
422 },
423}
424
425#[derive(Debug)]
430struct DocumentInner<'buf> {
431 source: &'buf str,
433 paths: PathTable<'buf>,
435}
436
437#[derive(Debug, Default)]
439struct PathTable<'buf> {
440 entries: Vec<PathEntry<'buf>>,
442}
443
444impl<'buf> PathTable<'buf> {
445 fn push(&mut self, entry: PathEntry<'buf>) {
446 self.entries.push(entry);
447 }
448
449 fn path_of(&self, element: &Element<'buf>) -> Path {
460 let mut entries: Vec<&PathEntry<'buf>> = Vec::new();
461 let mut elem_id = element.id;
462
463 loop {
465 let entry = self
466 .entries
467 .get(elem_id.0)
468 .expect("ElemId always refers to a valid PathEntry");
469
470 match entry {
471 PathEntry::Root => {
472 entries.push(entry);
473 break;
474 }
475 PathEntry::Field { parent, key: _ } | PathEntry::Item { parent, index: _ } => {
476 entries.push(entry);
477 elem_id = *parent;
478 }
479 }
480 }
481
482 entries.reverse();
484
485 let mut out = String::with_capacity(30);
486
487 for entry in entries {
488 let res = match entry {
489 PathEntry::Root => write!(out, "$"),
490 PathEntry::Field { parent: _, key } => {
491 write!(out, ".{}", key.as_unescaped_str())
492 }
493 PathEntry::Item { parent: _, index } => {
494 write!(out, "[{index}]")
497 }
498 };
499
500 res.expect("Writing to a String can only fail if the system runs out of heap memory");
501 }
502
503 Path(out)
504 }
505}
506
507#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
508pub struct Path(String);
509
510impl Path {
511 pub fn into_string(self) -> String {
512 self.0
513 }
514
515 pub fn as_str(&self) -> &str {
516 &self.0
517 }
518
519 pub fn components(&self) -> Components<'_> {
524 Components::over(&self.0)
525 }
526}
527
528#[derive(Clone, Copy, Debug, PartialEq, Eq)]
530pub enum Component<'a> {
531 Member(&'a str),
533 Index(&'a str),
535}
536
537#[derive(Clone, Debug)]
539pub struct Components<'a> {
540 rest: &'a str,
541}
542
543impl<'a> Components<'a> {
544 pub(crate) fn over(path: &'a str) -> Self {
546 Self {
547 rest: path.strip_prefix('$').unwrap_or(path),
548 }
549 }
550}
551
552impl<'a> Iterator for Components<'a> {
553 type Item = Component<'a>;
554
555 fn next(&mut self) -> Option<Self::Item> {
556 if let Some(after) = self.rest.strip_prefix('.') {
557 let end = after.find(['.', '[']).unwrap_or(after.len());
559 let (name, tail) = after.split_at(end);
560 self.rest = tail;
561 Some(Component::Member(name))
562 } else if let Some(after) = self.rest.strip_prefix('[') {
563 let end = after.find(']').unwrap_or(after.len());
565 let (index, tail) = after.split_at(end);
566 self.rest = tail.strip_prefix(']').unwrap_or(tail);
567 Some(Component::Index(index))
568 } else {
569 None
571 }
572 }
573}
574
575impl PartialEq<str> for Path {
576 fn eq(&self, other: &str) -> bool {
577 self.0 == other
578 }
579}
580
581impl PartialEq<&str> for Path {
582 fn eq(&self, other: &&str) -> bool {
583 self.0 == *other
584 }
585}
586
587impl fmt::Debug for Path {
588 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
589 f.write_str(&self.0)
590 }
591}
592
593impl fmt::Display for Path {
594 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
595 fmt::Display::fmt(&self.0, f)
596 }
597}
598
599#[derive(Debug)]
601pub struct PathSet<'set>(BTreeSet<&'set Path>);
602
603impl<'set> PathSet<'set> {
604 pub(crate) fn new(paths: BTreeSet<&'set Path>) -> Self {
605 Self(paths)
606 }
607
608 pub fn to_strings(&self) -> Vec<String> {
610 self.0.iter().map(ToString::to_string).collect()
611 }
612
613 pub fn into_strings(self) -> Vec<String> {
615 self.0.into_iter().map(ToString::to_string).collect()
616 }
617
618 pub fn is_empty(&self) -> bool {
620 self.0.is_empty()
621 }
622
623 pub fn len(&self) -> usize {
625 self.0.len()
626 }
627
628 pub fn iter(&self) -> btree_set::Iter<'_, &Path> {
630 self.0.iter()
631 }
632}
633
634impl<'set> IntoIterator for PathSet<'set> {
635 type Item = &'set Path;
636
637 type IntoIter = btree_set::IntoIter<&'set Path>;
638
639 fn into_iter(self) -> Self::IntoIter {
640 self.0.into_iter()
641 }
642}
643
644impl<'a, 'set> IntoIterator for &'a PathSet<'set> {
645 type Item = &'a &'set Path;
646
647 type IntoIter = btree_set::Iter<'a, &'set Path>;
648
649 fn into_iter(self) -> Self::IntoIter {
650 self.0.iter()
651 }
652}
653
654#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
655pub enum ValueKind {
656 Null,
657 Bool,
658 Number,
659 String,
660 Array,
661 Object,
662}
663
664impl fmt::Display for ValueKind {
665 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
666 match self {
667 ValueKind::Null => write!(f, "null"),
668 ValueKind::Bool => write!(f, "bool"),
669 ValueKind::Number => write!(f, "number"),
670 ValueKind::String => write!(f, "string"),
671 ValueKind::Array => write!(f, "array"),
672 ValueKind::Object => write!(f, "object"),
673 }
674 }
675}
676
677impl<'buf> Value<'buf> {
678 pub fn kind(&self) -> ValueKind {
679 match self {
680 Value::Null => ValueKind::Null,
681 Value::True | Value::False => ValueKind::Bool,
682 Value::String(_) => ValueKind::String,
683 Value::Number(_) => ValueKind::Number,
684 Value::Array(_) => ValueKind::Array,
685 Value::Object(_) => ValueKind::Object,
686 }
687 }
688
689 pub fn is_null(&self) -> bool {
690 matches!(self, Value::Null)
691 }
692
693 pub fn is_array(&self) -> bool {
695 matches!(self, Value::Array(..))
696 }
697
698 pub fn is_object(&self) -> bool {
700 matches!(self, Value::Object(..))
701 }
702
703 pub fn is_scalar(&self) -> bool {
705 matches!(
706 self,
707 Value::Null | Value::True | Value::False | Value::String(_) | Value::Number(_)
708 )
709 }
710
711 pub fn as_array(&self) -> Option<&[Element<'buf>]> {
712 if let Value::Array(elems) = self {
713 Some(elems)
714 } else {
715 None
716 }
717 }
718
719 pub fn as_number(&self) -> Option<&str> {
720 if let Value::Number(s) = self {
721 Some(s)
722 } else {
723 None
724 }
725 }
726
727 pub fn to_raw_str(&self) -> Option<RawStr<'buf>> {
729 if let Value::String(s) = self {
730 Some(*s)
731 } else {
732 None
733 }
734 }
735
736 pub fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
738 if let Value::Object(fields) = self {
739 Some(fields)
740 } else {
741 None
742 }
743 }
744}
745
746#[derive(Clone, Debug, Eq, PartialEq)]
748pub struct Field<'buf> {
749 key_span: Span,
751 element: Element<'buf>,
753}
754
755impl<'buf> Field<'buf> {
756 pub fn into_element(self) -> Element<'buf> {
758 self.element
759 }
760
761 pub fn element(&self) -> &Element<'buf> {
763 &self.element
764 }
765
766 pub fn key_span(&self) -> Span {
767 self.key_span
768 }
769
770 pub fn full_span(&self) -> Span {
784 Span {
785 start: self.key_span.start,
786 end: self.element.full_span_end,
787 }
788 }
789
790 #[expect(
792 clippy::arithmetic_side_effects,
793 reason = "key_span always spans a quoted string, so +1/-1 to strip the surrounding quote bytes is safe"
794 )]
795 #[expect(
796 clippy::string_slice,
797 reason = "key_span is produced by the parser from the same source; +1/-1 strips the ASCII quote bytes"
798 )]
799 #[expect(
800 clippy::as_conversions,
801 reason = "The index is guaranteed within bounds by the parser"
802 )]
803 pub fn key(&self) -> RawStr<'buf> {
804 let src = self.element.source();
805 let s = &src[self.key_span.start as usize + 1..self.key_span.end as usize - 1];
806 RawStr::from_str(s)
807 }
808
809 #[expect(
811 clippy::string_slice,
812 reason = "spans are produced by the parser from the same source, so slices are always valid"
813 )]
814 #[expect(
815 clippy::as_conversions,
816 reason = "The index is guaranteed within bounds by the parser"
817 )]
818 pub fn source_json(&self) -> &'buf str {
819 let src = self.element.source();
820 &src[self.key_span.start as usize..self.element.span.end as usize]
821 }
822}
823
824pub type RawMap<'buf> = BTreeMap<RawStr<'buf>, Element<'buf>>;
825pub type RawRefMap<'a, 'buf> = BTreeMap<RawStr<'buf>, &'a Element<'buf>>;
826
827#[expect(dead_code, reason = "pending use in `tariff::lint`")]
828pub(crate) trait FieldsIntoExt<'buf> {
829 fn into_map(self) -> RawMap<'buf>;
830}
831
832impl<'buf> FieldsIntoExt<'buf> for Vec<Field<'buf>> {
833 fn into_map(self) -> RawMap<'buf> {
834 self.into_iter()
835 .map(|field| (field.key(), field.into_element()))
836 .collect()
837 }
838}
839
840#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
842pub struct RawStr<'buf>(&'buf str);
843
844impl Borrow<str> for RawStr<'_> {
846 fn borrow(&self) -> &str {
847 self.0
848 }
849}
850
851impl Borrow<str> for &RawStr<'_> {
853 fn borrow(&self) -> &str {
854 self.0
855 }
856}
857
858impl<'buf> RawStr<'buf> {
859 fn from_str(source: &'buf str) -> Self {
860 Self(source)
861 }
862
863 pub fn eq_escape_aware(&self, other: &str) -> Result<bool, decode::Warning> {
870 decode::eq(self.0, other)
871 }
872
873 pub fn eq_any_escape_aware(&self, other: &[&str]) -> bool {
878 other
879 .iter()
880 .any(|s| decode::eq(self.0, s).ok().unwrap_or(false))
881 }
882
883 pub fn eq_any_escape_aware_ignore_ascii_case(&self, other: &[&str]) -> bool {
885 other.iter().any(|s| {
886 decode::eq_ignore_ascii_case(self.0, s)
887 .ok()
888 .unwrap_or(false)
889 })
890 }
891
892 pub fn as_unescaped_str(&self) -> &'buf str {
894 self.0
895 }
896
897 pub fn decode_escapes(&self) -> CaveatDeferred<Cow<'_, str>, decode::Warning> {
899 decode::from_raw(self.0)
900 }
901
902 pub fn has_escapes(&self, elem: &Element<'buf>) -> Caveat<PendingStr<'buf>, decode::Warning> {
904 decode::analyze(self.0, elem)
905 }
906
907 pub fn lexical_issues(&self) -> LexicalIssues {
913 decode::lexical_issues(self.0)
914 }
915}
916
917#[derive(Clone, Copy, Debug, Eq, PartialEq)]
920pub struct LexicalIssues {
921 pub escapes: bool,
923
924 pub non_printable_ascii: bool,
927}
928
929pub enum PendingStr<'buf> {
931 NoEscapes(&'buf str),
933
934 HasEscapes(EscapeStr<'buf>),
936}
937
938pub struct EscapeStr<'buf>(&'buf str);
940
941impl<'buf> EscapeStr<'buf> {
942 pub fn decode_escapes(&self) -> CaveatDeferred<Cow<'buf, str>, decode::Warning> {
943 decode::from_raw(self.0)
944 }
945
946 pub fn into_raw(self) -> &'buf str {
948 self.0
949 }
950}