1pub mod decode;
22mod parser;
23pub(crate) mod walk;
24pub mod write;
25
26#[cfg(test)]
27pub(crate) mod test;
28
29#[cfg(test)]
30mod test_line_col;
31
32#[cfg(test)]
33mod test_path;
34
35#[cfg(test)]
36mod test_path_matches_glob;
37
38#[cfg(test)]
39mod test_source_json;
40
41use std::{
42 borrow::{Borrow, Cow},
43 collections::{btree_set, BTreeMap, BTreeSet},
44 fmt::{self, Write as _},
45 rc::Rc,
46};
47
48use crate::warning::{Caveat, IntoCaveat, Set, Verdict};
49use crate::{
50 string,
51 warning::{self, CaveatDeferred},
52};
53
54pub(crate) use parser::parse;
55pub use parser::{Error, ErrorKind as ParseErrorKind};
56
57pub fn parse_object(json: &str) -> Result<Document<'_>, ParseError> {
62 let json = string::ReasonableLen::new(json).map_err(|_e| ParseError::SizeExceedsMax)?;
63 let doc = parse(json).map_err(ParseError::Json)?;
64
65 if !doc.root().is_object() {
66 return Err(ParseError::ShouldBeAnObject);
67 }
68
69 Ok(doc)
70}
71
72#[derive(Debug)]
73pub enum ParseError {
74 Json(Error),
76
77 ShouldBeAnObject,
79
80 SizeExceedsMax,
82}
83
84impl fmt::Display for ParseError {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self {
87 Self::Json(error) => write!(f, "{error}"),
88 Self::ShouldBeAnObject => f.write_str("The CDR should be an object."),
89 Self::SizeExceedsMax => write!(
90 f,
91 "The input `&str` exceeds the reasonable maximum `{} MB`.",
92 string::ReasonableLen::FACTOR
93 ),
94 }
95 }
96}
97
98impl std::error::Error for ParseError {
99 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
100 match &self {
101 ParseError::Json(err) => Some(err),
102 ParseError::ShouldBeAnObject | ParseError::SizeExceedsMax => None,
103 }
104 }
105}
106
107#[doc(hidden)]
110#[macro_export]
111macro_rules! required_field_or_bail {
112 ($elem:expr, $fields:expr, $field_name:literal, $warnings:expr) => {
113 match $fields.get($field_name) {
114 Some(field_elem) => field_elem,
115 None => {
116 return $warnings.bail(
117 $elem,
118 Warning::FieldRequired {
119 field_name: $field_name.into(),
120 },
121 );
122 }
123 }
124 };
125}
126
127#[doc(hidden)]
130#[macro_export]
131macro_rules! required_field {
132 ($elem:expr, $fields:expr, $field_name:literal, $warnings:expr) => {{
133 let field = $fields.get($field_name);
134
135 if field.is_none() {
136 $warnings.insert(
137 $elem,
138 Warning::FieldRequired {
139 field_name: $field_name.into(),
140 },
141 );
142 }
143
144 field
145 }};
146}
147
148#[doc(hidden)]
151#[macro_export]
152macro_rules! expect_object_or_bail {
153 ($elem:expr, $warnings:expr) => {
154 match $elem.as_object_fields() {
155 Some(fields) => fields,
156 None => {
157 return $warnings.bail(
158 $elem,
159 Warning::FieldInvalidType {
160 expected_type: json::ValueKind::Object,
161 },
162 );
163 }
164 }
165 };
166}
167
168#[doc(hidden)]
171#[macro_export]
172macro_rules! expect_array_or_bail {
173 ($elem:expr, $warnings:expr) => {
174 match $elem.as_array() {
175 Some(fields) => fields,
176 None => {
177 return $warnings.bail(
178 $elem,
179 Warning::FieldInvalidType {
180 expected_type: json::ValueKind::Array,
181 },
182 );
183 }
184 }
185 };
186}
187
188#[doc(hidden)]
192#[macro_export]
193macro_rules! parse_required_or_bail {
194 ($elem:expr, $fields:expr, $elem_name:literal, $target:ty, $warnings:expr) => {{
195 #[expect(clippy::allow_attributes, reason = "The allow attribute is needed here as the callers scope determines if the imports are used or not")]
196 #[allow(
197 unused,
198 reason = "The macro uses the import but maybe the outside scope does too."
199 )]
200 use $crate::json::FromJson;
201 use $crate::warning::GatherWarnings as _;
202
203 let elem = $crate::required_field_or_bail!($elem, $fields, $elem_name, $warnings);
204 <$target as FromJson>::from_json(elem)?.gather_warnings_into(&mut $warnings)
205 }};
206}
207
208#[doc(hidden)]
212#[macro_export]
213macro_rules! parse_required {
214 ($elem:expr, $fields:expr, $elem_name:literal, $target:ty, $warnings:expr) => {{
215 #[expect(
216 clippy::allow_attributes,
217 reason = "The allow attribute is needed here as the callers scope determines if the imports are used or not"
218 )]
219 #[allow(
220 unused,
221 reason = "The macro uses the import but maybe the outside scope does too."
222 )]
223 use $crate::json::FromJson;
224 use $crate::warning::GatherWarnings as _;
225
226 let elem = $crate::required_field!($elem, $fields, $elem_name, $warnings);
227
228 if let Some(elem) = elem {
229 let value =
230 <$target as FromJson>::from_json(elem)?.gather_warnings_into(&mut $warnings);
231 Some(value)
232 } else {
233 None
234 }
235 }};
236}
237
238#[doc(hidden)]
241#[macro_export]
242macro_rules! parse_nullable_or_bail {
243 ($fields:expr, $elem_name:literal, $target:ty, $warnings:expr) => {{
244 #[expect(
245 clippy::allow_attributes,
246 reason = "The allow attribute is needed here as the callers scope determines if the imports are used or not"
247 )]
248 #[allow(
249 unused,
250 reason = "The macro uses the import but maybe the outside scope does too."
251 )]
252 use $crate::json::FromJson as _;
253 use $crate::warning::GatherWarnings as _;
254
255 match $fields.get($elem_name) {
256 Some(elem) => Option::<$target>::from_json(elem)?.gather_warnings_into(&mut $warnings),
257 None => None,
258 }
259 }};
260}
261
262#[derive(Clone, Debug)]
264pub struct Document<'buf> {
265 inner: Rc<DocumentInner<'buf>>,
267 root: Element<'buf>,
269}
270
271impl<'buf> Document<'buf> {
272 pub fn source(&self) -> &'buf str {
274 self.inner.source
275 }
276
277 pub fn root(&self) -> &Element<'buf> {
279 &self.root
280 }
281}
282
283#[derive(Clone, Debug)]
288pub struct Element<'buf> {
289 doc: Rc<DocumentInner<'buf>>,
291 id: ElemId,
293 span: Span,
295 full_span_end: u32,
298 value: Value<'buf>,
300}
301
302impl PartialEq for Element<'_> {
303 fn eq(&self, other: &Self) -> bool {
304 self.id == other.id
305 && self.span == other.span
306 && self.full_span_end == other.full_span_end
307 && self.value == other.value
308 }
309}
310
311impl Eq for Element<'_> {}
312
313impl<'buf> Element<'buf> {
314 pub fn id(&self) -> ElemId {
315 self.id
316 }
317
318 pub fn span(&self) -> Span {
319 self.span
320 }
321
322 pub fn full_span(&self) -> Span {
338 Span {
339 start: self.span.start,
340 end: self.full_span_end,
341 }
342 }
343
344 pub fn value(&self) -> &Value<'buf> {
345 &self.value
346 }
347
348 pub fn path(&self) -> Path {
352 self.doc.paths.path_of(self)
353 }
354
355 #[expect(
357 clippy::string_slice,
358 reason = "spans are produced by the parser from the same source, so slices are always valid"
359 )]
360 #[expect(
361 clippy::as_conversions,
362 reason = "The index is guaranteed within bounds by the parser"
363 )]
364 pub fn source_json_value(&self) -> &'buf str {
365 &self.doc.source[self.span.start as usize..self.span.end as usize]
366 }
367
368 pub fn source(&self) -> &'buf str {
370 self.doc.source
371 }
372
373 #[expect(
375 clippy::string_slice,
376 reason = "spans are produced by the parser from the same source, so slices are always valid"
377 )]
378 #[expect(
379 clippy::as_conversions,
380 reason = "The index is guaranteed within bounds by the parser"
381 )]
382 pub fn location(&self) -> Location {
383 let source = self.doc.source;
384
385 let lead_in = &source[..self.span.start as usize];
387 line_col(lead_in)
388 }
389
390 pub fn as_value(&self) -> &Value<'buf> {
392 &self.value
393 }
394
395 pub fn to_raw_str(&self) -> Option<RawStr<'buf>> {
397 self.value.to_raw_str()
398 }
399
400 pub fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
402 self.value.as_object_fields()
403 }
404
405 pub fn as_array(&self) -> Option<&[Element<'buf>]> {
406 self.value.as_array()
407 }
408
409 pub fn as_number_str(&self) -> Option<&str> {
410 self.value.as_number()
411 }
412
413 pub fn is_null(&self) -> bool {
415 self.value.is_null()
416 }
417
418 pub fn is_object(&self) -> bool {
420 self.value.is_object()
421 }
422
423 pub fn is_array(&self) -> bool {
425 self.value.is_array()
426 }
427}
428
429#[derive(Clone, Debug, Eq, PartialEq)]
431pub enum Value<'buf> {
432 Null,
434 True,
436 False,
438 String(RawStr<'buf>),
440 Number(&'buf str),
442 Array(Vec<Element<'buf>>),
444 Object(Vec<Field<'buf>>),
446}
447
448impl fmt::Display for Value<'_> {
449 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450 match self {
451 Self::Null => write!(f, "null"),
452 Self::True => write!(f, "true"),
453 Self::False => write!(f, "false"),
454 Self::String(s) => write!(f, "{}", s.as_unescaped_str()),
455 Self::Number(s) => write!(f, "{s}"),
456 Self::Array(..) => f.write_str("[...]"),
457 Self::Object(..) => f.write_str("{...}"),
458 }
459 }
460}
461
462#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
464pub struct Span {
465 pub start: u32,
467 pub end: u32,
469}
470
471impl Span {
472 fn new(start: u32, end: u32) -> Self {
473 Self { start, end }
474 }
475}
476
477#[derive(Clone, Debug)]
479pub struct Location {
480 pub line: u32,
482
483 pub col: u32,
485}
486
487impl From<(u32, u32)> for Location {
488 fn from(value: (u32, u32)) -> Self {
489 Self {
490 line: value.0,
491 col: value.1,
492 }
493 }
494}
495
496impl From<Location> for (u32, u32) {
497 fn from(value: Location) -> Self {
498 (value.line, value.col)
499 }
500}
501
502impl PartialEq<(u32, u32)> for Location {
503 fn eq(&self, other: &(u32, u32)) -> bool {
504 self.line == other.0 && self.col == other.1
505 }
506}
507
508impl fmt::Display for Location {
509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 write!(f, "{}:{}", self.line, self.col)
511 }
512}
513
514pub fn line_col(s: &str) -> Location {
518 let mut chars = s.chars().rev();
519 let mut line = 0_u32;
520 let mut col = 0_u32;
521
522 for c in chars.by_ref() {
527 if c == '\n' {
529 let Some(n) = line.checked_add(1) else {
530 break;
531 };
532 line = n;
533 break;
534 }
535 let Some(n) = col.checked_add(1) else {
536 break;
537 };
538 col = n;
539 }
540
541 for c in chars {
543 if c == '\n' {
544 let Some(n) = line.checked_add(1) else {
545 break;
546 };
547 line = n;
548 }
549 }
550
551 Location { line, col }
552}
553
554#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
559pub struct ElemId(usize);
560
561#[derive(Debug)]
563enum PathEntry<'buf> {
564 Root,
566 Field {
568 parent: ElemId,
570 key: RawStr<'buf>,
572 },
573 Item {
575 parent: ElemId,
577 index: u32,
579 },
580}
581
582#[derive(Debug)]
587struct DocumentInner<'buf> {
588 source: &'buf str,
590 paths: PathTable<'buf>,
592}
593
594#[derive(Debug, Default)]
596struct PathTable<'buf> {
597 entries: Vec<PathEntry<'buf>>,
599}
600
601impl<'buf> PathTable<'buf> {
602 fn push(&mut self, entry: PathEntry<'buf>) {
603 self.entries.push(entry);
604 }
605
606 fn path_of(&self, element: &Element<'buf>) -> Path {
617 let mut entries: Vec<&PathEntry<'buf>> = Vec::new();
618 let mut elem_id = element.id;
619
620 loop {
622 let entry = self
623 .entries
624 .get(elem_id.0)
625 .expect("ElemId always refers to a valid PathEntry");
626
627 match entry {
628 PathEntry::Root => {
629 entries.push(entry);
630 break;
631 }
632 PathEntry::Field { parent, key: _ } | PathEntry::Item { parent, index: _ } => {
633 entries.push(entry);
634 elem_id = *parent;
635 }
636 }
637 }
638
639 entries.reverse();
641
642 let mut out = String::with_capacity(30);
643
644 for entry in entries {
645 let res = match entry {
646 PathEntry::Root => write!(out, "$"),
647 PathEntry::Field { parent: _, key } => {
648 write!(out, ".{}", key.as_unescaped_str())
649 }
650 PathEntry::Item { parent: _, index } => {
651 write!(out, "[{index}]")
654 }
655 };
656
657 res.expect("Writing to a String can only fail if the system runs out of heap memory");
658 }
659
660 Path(out)
661 }
662}
663
664#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
665pub struct Path(String);
666
667impl Path {
668 pub fn into_string(self) -> String {
669 self.0
670 }
671
672 pub fn as_str(&self) -> &str {
673 &self.0
674 }
675
676 pub fn components(&self) -> Components<'_> {
681 Components::over(&self.0)
682 }
683}
684
685#[derive(Clone, Copy, Debug, PartialEq, Eq)]
687pub enum Component<'a> {
688 Member(&'a str),
690 Index(&'a str),
692}
693
694#[derive(Clone, Debug)]
696pub struct Components<'a> {
697 rest: &'a str,
698}
699
700impl<'a> Components<'a> {
701 pub(crate) fn over(path: &'a str) -> Self {
703 Self {
704 rest: path.strip_prefix('$').unwrap_or(path),
705 }
706 }
707}
708
709impl<'a> Iterator for Components<'a> {
710 type Item = Component<'a>;
711
712 fn next(&mut self) -> Option<Self::Item> {
713 if let Some(after) = self.rest.strip_prefix('.') {
714 let end = after.find(['.', '[']).unwrap_or(after.len());
716 let (name, tail) = after.split_at(end);
717 self.rest = tail;
718 Some(Component::Member(name))
719 } else if let Some(after) = self.rest.strip_prefix('[') {
720 let end = after.find(']').unwrap_or(after.len());
722 let (index, tail) = after.split_at(end);
723 self.rest = tail.strip_prefix(']').unwrap_or(tail);
724 Some(Component::Index(index))
725 } else {
726 None
728 }
729 }
730}
731
732impl PartialEq<str> for Path {
733 fn eq(&self, other: &str) -> bool {
734 self.0 == other
735 }
736}
737
738impl PartialEq<&str> for Path {
739 fn eq(&self, other: &&str) -> bool {
740 self.0 == *other
741 }
742}
743
744impl fmt::Debug for Path {
745 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
746 f.write_str(&self.0)
747 }
748}
749
750impl fmt::Display for Path {
751 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
752 fmt::Display::fmt(&self.0, f)
753 }
754}
755
756#[derive(Debug)]
758pub struct PathSet<'set>(BTreeSet<&'set Path>);
759
760impl<'set> PathSet<'set> {
761 pub(crate) fn new(paths: BTreeSet<&'set Path>) -> Self {
762 Self(paths)
763 }
764
765 pub fn to_strings(&self) -> Vec<String> {
767 self.0.iter().map(ToString::to_string).collect()
768 }
769
770 pub fn into_strings(self) -> Vec<String> {
772 self.0.into_iter().map(ToString::to_string).collect()
773 }
774
775 pub fn is_empty(&self) -> bool {
777 self.0.is_empty()
778 }
779
780 pub fn len(&self) -> usize {
782 self.0.len()
783 }
784
785 pub fn iter(&self) -> btree_set::Iter<'_, &Path> {
787 self.0.iter()
788 }
789}
790
791impl<'set> IntoIterator for PathSet<'set> {
792 type Item = &'set Path;
793
794 type IntoIter = btree_set::IntoIter<&'set Path>;
795
796 fn into_iter(self) -> Self::IntoIter {
797 self.0.into_iter()
798 }
799}
800
801impl<'a, 'set> IntoIterator for &'a PathSet<'set> {
802 type Item = &'a &'set Path;
803
804 type IntoIter = btree_set::Iter<'a, &'set Path>;
805
806 fn into_iter(self) -> Self::IntoIter {
807 self.0.iter()
808 }
809}
810
811#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
812pub enum ValueKind {
813 Null,
814 Bool,
815 Number,
816 String,
817 Array,
818 Object,
819}
820
821impl fmt::Display for ValueKind {
822 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
823 match self {
824 ValueKind::Null => write!(f, "null"),
825 ValueKind::Bool => write!(f, "bool"),
826 ValueKind::Number => write!(f, "number"),
827 ValueKind::String => write!(f, "string"),
828 ValueKind::Array => write!(f, "array"),
829 ValueKind::Object => write!(f, "object"),
830 }
831 }
832}
833
834impl<'buf> Value<'buf> {
835 pub fn kind(&self) -> ValueKind {
836 match self {
837 Value::Null => ValueKind::Null,
838 Value::True | Value::False => ValueKind::Bool,
839 Value::String(_) => ValueKind::String,
840 Value::Number(_) => ValueKind::Number,
841 Value::Array(_) => ValueKind::Array,
842 Value::Object(_) => ValueKind::Object,
843 }
844 }
845
846 pub fn is_null(&self) -> bool {
847 matches!(self, Value::Null)
848 }
849
850 pub fn is_array(&self) -> bool {
852 matches!(self, Value::Array(..))
853 }
854
855 pub fn is_object(&self) -> bool {
857 matches!(self, Value::Object(..))
858 }
859
860 pub fn is_scalar(&self) -> bool {
862 matches!(
863 self,
864 Value::Null | Value::True | Value::False | Value::String(_) | Value::Number(_)
865 )
866 }
867
868 pub fn as_array(&self) -> Option<&[Element<'buf>]> {
869 if let Value::Array(elems) = self {
870 Some(elems)
871 } else {
872 None
873 }
874 }
875
876 pub fn as_number(&self) -> Option<&str> {
877 if let Value::Number(s) = self {
878 Some(s)
879 } else {
880 None
881 }
882 }
883
884 pub fn to_raw_str(&self) -> Option<RawStr<'buf>> {
886 if let Value::String(s) = self {
887 Some(*s)
888 } else {
889 None
890 }
891 }
892
893 pub fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
895 if let Value::Object(fields) = self {
896 Some(fields)
897 } else {
898 None
899 }
900 }
901}
902
903#[derive(Clone, Debug, Eq, PartialEq)]
905pub struct Field<'buf> {
906 key_span: Span,
908 element: Element<'buf>,
910}
911
912impl<'buf> Field<'buf> {
913 pub fn into_element(self) -> Element<'buf> {
915 self.element
916 }
917
918 pub fn element(&self) -> &Element<'buf> {
920 &self.element
921 }
922
923 pub fn key_span(&self) -> Span {
924 self.key_span
925 }
926
927 pub fn full_span(&self) -> Span {
941 Span {
942 start: self.key_span.start,
943 end: self.element.full_span_end,
944 }
945 }
946
947 #[expect(
949 clippy::arithmetic_side_effects,
950 reason = "key_span always spans a quoted string, so +1/-1 to strip the surrounding quote bytes is safe"
951 )]
952 #[expect(
953 clippy::string_slice,
954 reason = "key_span is produced by the parser from the same source; +1/-1 strips the ASCII quote bytes"
955 )]
956 #[expect(
957 clippy::as_conversions,
958 reason = "The index is guaranteed within bounds by the parser"
959 )]
960 pub fn key(&self) -> RawStr<'buf> {
961 let src = self.element.source();
962 let s = &src[self.key_span.start as usize + 1..self.key_span.end as usize - 1];
963 RawStr::from_str(s)
964 }
965
966 #[expect(
968 clippy::string_slice,
969 reason = "spans are produced by the parser from the same source, so slices are always valid"
970 )]
971 #[expect(
972 clippy::as_conversions,
973 reason = "The index is guaranteed within bounds by the parser"
974 )]
975 pub fn source_json(&self) -> &'buf str {
976 let src = self.element.source();
977 &src[self.key_span.start as usize..self.element.span.end as usize]
978 }
979}
980
981pub type RawMap<'buf> = BTreeMap<RawStr<'buf>, Element<'buf>>;
982pub type RawRefMap<'a, 'buf> = BTreeMap<RawStr<'buf>, &'a Element<'buf>>;
983
984#[expect(dead_code, reason = "pending use in `tariff::lint`")]
985pub(crate) trait FieldsIntoExt<'buf> {
986 fn into_map(self) -> RawMap<'buf>;
987}
988
989pub(crate) trait FieldsAsExt<'buf> {
990 fn as_raw_map(&self) -> RawRefMap<'_, 'buf>;
991 fn find_field(&self, key: &str) -> Option<&Field<'buf>>;
992}
993
994impl<'buf> FieldsIntoExt<'buf> for Vec<Field<'buf>> {
995 fn into_map(self) -> RawMap<'buf> {
996 self.into_iter()
997 .map(|field| (field.key(), field.into_element()))
998 .collect()
999 }
1000}
1001
1002impl<'buf> FieldsAsExt<'buf> for Vec<Field<'buf>> {
1003 fn as_raw_map(&self) -> RawRefMap<'_, 'buf> {
1004 self.iter()
1005 .map(|field| (field.key(), field.element()))
1006 .collect()
1007 }
1008
1009 fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
1010 self.iter()
1011 .find(|field| field.key().eq_escape_aware(key).ok().unwrap_or(false))
1012 }
1013}
1014
1015impl<'buf> FieldsAsExt<'buf> for [Field<'buf>] {
1016 fn as_raw_map(&self) -> RawRefMap<'_, 'buf> {
1017 self.iter()
1018 .map(|field| (field.key(), field.element()))
1019 .collect()
1020 }
1021
1022 fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
1023 self.iter()
1024 .find(|field| field.key().eq_escape_aware(key).ok().unwrap_or(false))
1025 }
1026}
1027
1028#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
1030pub struct RawStr<'buf>(&'buf str);
1031
1032impl Borrow<str> for RawStr<'_> {
1034 fn borrow(&self) -> &str {
1035 self.0
1036 }
1037}
1038
1039impl Borrow<str> for &RawStr<'_> {
1041 fn borrow(&self) -> &str {
1042 self.0
1043 }
1044}
1045
1046impl<'buf> RawStr<'buf> {
1047 fn from_str(source: &'buf str) -> Self {
1048 Self(source)
1049 }
1050
1051 pub fn eq_escape_aware(&self, other: &str) -> Result<bool, decode::Warning> {
1058 decode::eq(self.0, other)
1059 }
1060
1061 pub fn eq_any_escape_aware(&self, other: &[&str]) -> bool {
1066 other
1067 .iter()
1068 .any(|s| decode::eq(self.0, s).ok().unwrap_or(false))
1069 }
1070
1071 pub fn eq_any_escape_aware_ignore_ascii_case(&self, other: &[&str]) -> bool {
1073 other.iter().any(|s| {
1074 decode::eq_ignore_ascii_case(self.0, s)
1075 .ok()
1076 .unwrap_or(false)
1077 })
1078 }
1079
1080 pub fn as_unescaped_str(&self) -> &'buf str {
1082 self.0
1083 }
1084
1085 pub fn decode_escapes(&self) -> CaveatDeferred<Cow<'_, str>, decode::Warning> {
1087 decode::from_raw(self.0)
1088 }
1089
1090 pub fn has_escapes(&self, elem: &Element<'buf>) -> Caveat<PendingStr<'buf>, decode::Warning> {
1092 decode::analyze(self.0, elem)
1093 }
1094}
1095
1096pub enum PendingStr<'buf> {
1098 NoEscapes(&'buf str),
1100
1101 HasEscapes(EscapeStr<'buf>),
1103}
1104
1105pub struct EscapeStr<'buf>(&'buf str);
1107
1108impl<'buf> EscapeStr<'buf> {
1109 pub fn decode_escapes(&self) -> CaveatDeferred<Cow<'buf, str>, decode::Warning> {
1110 decode::from_raw(self.0)
1111 }
1112
1113 pub fn into_raw(self) -> &'buf str {
1115 self.0
1116 }
1117}
1118
1119pub(crate) trait FromJson<'buf>: Sized {
1124 type Warning: warning::Warning;
1126
1127 fn from_json(elem: &Element<'buf>) -> Verdict<Self, Self::Warning>;
1129}
1130
1131impl<'buf, T> FromJson<'buf> for Option<T>
1135where
1136 T: FromJson<'buf> + IntoCaveat,
1137{
1138 type Warning = T::Warning;
1139
1140 fn from_json(elem: &Element<'buf>) -> Verdict<Self, Self::Warning> {
1141 let value = elem.as_value();
1142
1143 if value.is_null() {
1144 Ok(None.into_caveat(Set::new()))
1145 } else {
1146 let v = T::from_json(elem)?;
1147 Ok(v.map(Some))
1148 }
1149 }
1150}