1use crate::error::{Error, Result};
22use crate::parser::meter::{AliasCost, CostTally, Meter};
23use crate::parser::{
24 Event, InternalDuplicateKeyPolicy, InternalMergeKeyPolicy, ParseConfig, Parser, ScalarStyle,
25 budget,
26};
27use crate::path::{QueryPath, QuerySegment, parse_query_path};
28use crate::prelude::IndexMap;
29use crate::prelude::*;
30use crate::prelude::{FxBuildHasher, FxHashMap};
31use core::hash::{Hash, Hasher};
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49#[non_exhaustive]
50pub enum TransformReason {
51 EscapeSequence,
55 LineFold,
58 TagResolution,
61 QuotedScalar,
64 AliasExpansion,
68}
69
70impl TransformReason {
71 #[must_use]
82 pub fn as_str(self) -> &'static str {
83 match self {
84 Self::EscapeSequence => "scalar contained escape sequences that required decoding",
85 Self::LineFold => "scalar spans multiple lines and required line folding",
86 Self::TagResolution => "tag resolution materialised a fresh representation",
87 Self::QuotedScalar => "double-quoted scalar with escapes produced an owned buffer",
88 Self::AliasExpansion => "scalar arrived via alias replay (`*anchor`)",
89 }
90 }
91}
92
93impl fmt::Display for TransformReason {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 f.write_str(self.as_str())
96 }
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
109pub enum BorrowedValue<'a> {
110 Null,
119 Bool(bool),
129 Number(crate::value::Number),
139 String(Cow<'a, str>),
150 Sequence(Vec<Self>),
160 Mapping(IndexMap<Cow<'a, str>, Self, FxBuildHasher>),
170}
171
172impl<'a> BorrowedValue<'a> {
173 #[must_use]
182 pub fn is_null(&self) -> bool {
183 matches!(self, Self::Null)
184 }
185
186 #[must_use]
197 pub fn as_str(&self) -> Option<&str> {
198 match self {
199 Self::String(s) => Some(s),
200 _ => None,
201 }
202 }
203
204 #[must_use]
214 pub fn as_i64(&self) -> Option<i64> {
215 match self {
216 Self::Number(n) => n.as_i64(),
217 _ => None,
218 }
219 }
220
221 #[must_use]
230 pub fn as_bool(&self) -> Option<bool> {
231 match self {
232 Self::Bool(b) => Some(*b),
233 _ => None,
234 }
235 }
236
237 #[must_use]
247 pub fn as_sequence(&self) -> Option<&[Self]> {
248 match self {
249 Self::Sequence(s) => Some(s),
250 _ => None,
251 }
252 }
253
254 #[must_use]
264 pub fn as_mapping(&self) -> Option<&IndexMap<Cow<'a, str>, Self, FxBuildHasher>> {
265 match self {
266 Self::Mapping(m) => Some(m),
267 _ => None,
268 }
269 }
270
271 #[must_use]
273 pub fn query_path(&self, path: &QueryPath) -> Vec<&Self> {
274 let mut results = Vec::new();
275 borrowed_query_recursive(self, path.segments(), 0, &mut results);
276 results
277 }
278
279 #[must_use]
296 pub fn query(&self, path: &str) -> Vec<&Self> {
297 let segments = parse_query_path(path);
298 let mut results = Vec::new();
299 borrowed_query_recursive(self, &segments, 0, &mut results);
300 results
301 }
302
303 #[must_use]
305 pub fn get_query_path(&self, path: &QueryPath) -> Option<&Self> {
306 let mut current = self;
307 for seg in path.segments() {
308 current = match seg {
309 QuerySegment::Key(key) => {
310 if let Self::Mapping(m) = current {
311 m.get(key.as_str())?
312 } else {
313 return None;
314 }
315 }
316 QuerySegment::Index(idx) => {
317 if let Self::Sequence(s) = current {
318 s.get(*idx)?
319 } else {
320 return None;
321 }
322 }
323 QuerySegment::Wildcard | QuerySegment::RecursiveDescent => {
324 return self.query_path(path).into_iter().next();
325 }
326 };
327 }
328 Some(current)
329 }
330
331 #[must_use]
341 pub fn get_path(&self, path: &str) -> Option<&Self> {
342 let segments = parse_query_path(path);
343 let mut current = self;
344 for seg in &segments {
345 current = match seg {
346 QuerySegment::Key(key) => {
347 if let Self::Mapping(m) = current {
348 m.get(key.as_str())?
349 } else {
350 return None;
351 }
352 }
353 QuerySegment::Index(idx) => {
354 if let Self::Sequence(s) = current {
355 s.get(*idx)?
356 } else {
357 return None;
358 }
359 }
360 QuerySegment::Wildcard | QuerySegment::RecursiveDescent => {
361 return self.query(path).into_iter().next();
362 }
363 };
364 }
365 Some(current)
366 }
367
368 #[must_use]
379 pub fn into_owned(self) -> crate::Value {
380 match self {
381 Self::Null => crate::Value::Null,
382 Self::Bool(b) => crate::Value::Bool(b),
383 Self::Number(n) => crate::Value::Number(n),
384 Self::String(s) => crate::Value::String(s.into_owned()),
385 Self::Sequence(seq) => {
386 crate::Value::Sequence(seq.into_iter().map(|v| v.into_owned()).collect())
387 }
388 Self::Mapping(map) => {
389 let mut m = crate::Mapping::with_capacity(map.len());
390 for (k, v) in map {
391 let _ = m.insert(k.into_owned(), v.into_owned());
392 }
393 crate::Value::Mapping(m)
394 }
395 }
396 }
397}
398
399impl Hash for BorrowedValue<'_> {
400 fn hash<H: Hasher>(&self, state: &mut H) {
401 core::mem::discriminant(self).hash(state);
402 match self {
403 Self::Null => {}
404 Self::Bool(b) => b.hash(state),
405 Self::Number(n) => n.hash(state),
406 Self::String(s) => s.hash(state),
407 Self::Sequence(seq) => seq.hash(state),
408 Self::Mapping(map) => {
409 state.write_usize(map.len());
410 for (k, v) in map {
411 k.hash(state);
412 v.hash(state);
413 }
414 }
415 }
416 }
417}
418
419impl serde_core::Serialize for BorrowedValue<'_> {
420 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
421 where
422 S: serde_core::Serializer,
423 {
424 match self {
425 Self::Null => serializer.serialize_none(),
426 Self::Bool(b) => serializer.serialize_bool(*b),
427 Self::Number(n) => match n {
428 crate::value::Number::Integer(i) => serializer.serialize_i64(*i),
429 #[cfg(feature = "lossless-u64")]
430 crate::value::Number::Unsigned(u) => serializer.serialize_u64(*u),
431 crate::value::Number::Float(f) => serializer.serialize_f64(*f),
432 },
433 Self::String(s) => serializer.serialize_str(s),
434 Self::Sequence(seq) => seq.serialize(serializer),
435 Self::Mapping(map) => {
436 use serde_core::ser::SerializeMap as _;
437 let mut m = serializer.serialize_map(Some(map.len()))?;
438 for (k, v) in map {
439 m.serialize_entry(k.as_ref(), v)?;
440 }
441 m.end()
442 }
443 }
444 }
445}
446
447impl PartialOrd for BorrowedValue<'_> {
448 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
449 Some(self.cmp(other))
450 }
451}
452
453impl Ord for BorrowedValue<'_> {
454 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
455 use core::cmp::Ordering;
456 let rank = |v: &Self| -> u8 {
457 match v {
458 Self::Null => 0,
459 Self::Bool(_) => 1,
460 Self::Number(_) => 2,
461 Self::String(_) => 3,
462 Self::Sequence(_) => 4,
463 Self::Mapping(_) => 5,
464 }
465 };
466 let r = rank(self).cmp(&rank(other));
467 if r != Ordering::Equal {
468 return r;
469 }
470 match (self, other) {
471 (Self::Null, Self::Null) => Ordering::Equal,
472 (Self::Bool(a), Self::Bool(b)) => a.cmp(b),
473 (Self::Number(a), Self::Number(b)) => a.cmp(b),
474 (Self::String(a), Self::String(b)) => a.cmp(b),
475 (Self::Sequence(a), Self::Sequence(b)) => a.cmp(b),
476 (Self::Mapping(a), Self::Mapping(b)) => a.len().cmp(&b.len()),
477 _ => Ordering::Equal,
478 }
479 }
480}
481
482fn borrowed_query_recursive<'a, 'b>(
484 value: &'b BorrowedValue<'a>,
485 segments: &[QuerySegment],
486 depth: usize,
487 results: &mut Vec<&'b BorrowedValue<'a>>,
488) {
489 if depth >= segments.len() {
490 results.push(value);
491 return;
492 }
493 match &segments[depth] {
494 QuerySegment::Key(key) => {
495 if let BorrowedValue::Mapping(m) = value
496 && let Some(child) = m.get(key.as_str())
497 {
498 borrowed_query_recursive(child, segments, depth + 1, results);
499 }
500 }
501 QuerySegment::Index(idx) => {
502 if let BorrowedValue::Sequence(s) = value
503 && let Some(child) = s.get(*idx)
504 {
505 borrowed_query_recursive(child, segments, depth + 1, results);
506 }
507 }
508 QuerySegment::Wildcard => match value {
509 BorrowedValue::Sequence(seq) => {
510 for item in seq {
511 borrowed_query_recursive(item, segments, depth + 1, results);
512 }
513 }
514 BorrowedValue::Mapping(map) => {
515 for (_, v) in map {
516 borrowed_query_recursive(v, segments, depth + 1, results);
517 }
518 }
519 _ => {}
520 },
521 QuerySegment::RecursiveDescent => {
522 let remaining = &segments[depth + 1..];
523 if !remaining.is_empty() {
524 borrowed_query_recursive(value, segments, depth + 1, results);
525 match value {
526 BorrowedValue::Sequence(seq) => {
527 for item in seq {
528 borrowed_query_recursive(item, segments, depth, results);
529 }
530 }
531 BorrowedValue::Mapping(map) => {
532 for (_, v) in map {
533 borrowed_query_recursive(v, segments, depth, results);
534 }
535 }
536 _ => {}
537 }
538 }
539 }
540 }
541}
542
543pub fn from_str_borrowed(input: &str) -> Result<BorrowedValue<'_>> {
559 from_str_borrowed_with_config(input, &crate::ParserConfig::default())
560}
561
562pub fn from_str_borrowed_with_config<'a>(
592 input: &'a str,
593 user_config: &crate::ParserConfig,
594) -> Result<BorrowedValue<'a>> {
595 let config = ParseConfig::from(user_config);
596 if input.len() > config.max_document_length {
597 return Err(Error::Parse(format!(
598 "document exceeds maximum length of {} bytes",
599 config.max_document_length
600 )));
601 }
602
603 let mut parser = Parser::with_max_events(input, config.max_events);
604 let mut builder = BorrowedBuilder::new(&config);
605
606 loop {
607 let event = parser
608 .next_event()
609 .map_err(|e| e.into_error(input, config.max_events))?;
610 if matches!(event, Event::StreamEnd) {
611 break;
612 }
613 builder.process(event)?;
614 }
615
616 let value = builder.into_value();
617 if !user_config.policies.is_empty() {
618 crate::policy::check_document(&user_config.policies, &value.clone().into_owned())?;
619 }
620 Ok(value)
621}
622
623enum Frame<'a> {
624 Sequence(Vec<BorrowedValue<'a>>, Option<String>),
625 MappingKey(
626 IndexMap<Cow<'a, str>, BorrowedValue<'a>, FxBuildHasher>,
627 Option<String>,
628 ),
629 MappingValue(
630 IndexMap<Cow<'a, str>, BorrowedValue<'a>, FxBuildHasher>,
631 Cow<'a, str>,
632 Option<String>,
633 ),
634}
635
636const MERGE_KEY: &str = "<<";
638
639struct BorrowedBuilder<'a, 'c> {
640 stack: Vec<Frame<'a>>,
641 result: Option<BorrowedValue<'a>>,
642 config: &'c ParseConfig,
643 depth: usize,
644 anchors: FxHashMap<String, (BorrowedValue<'a>, AliasCost)>,
649 meter: Meter,
652}
653
654impl<'a, 'c> BorrowedBuilder<'a, 'c> {
655 fn new(config: &'c ParseConfig) -> Self {
656 Self {
657 stack: Vec::new(),
658 result: None,
659 config,
660 depth: 0,
661 anchors: FxHashMap::default(),
662 meter: Meter::new(config),
663 }
664 }
665
666 fn into_value(self) -> BorrowedValue<'a> {
667 self.result.unwrap_or(BorrowedValue::Null)
668 }
669
670 fn resolve_scalar(
671 &self,
672 value: Cow<'a, str>,
673 style: ScalarStyle,
674 tag: Option<&(String, String)>,
675 ) -> BorrowedValue<'a> {
676 if is_core_string_tag(tag) || style != ScalarStyle::Plain {
688 return BorrowedValue::String(value);
689 }
690 let c = self.config;
691 match crate::streaming::resolve_plain_ext(
692 &value,
693 c.strict_booleans,
694 c.legacy_booleans,
695 c.no_schema,
696 c.legacy_octal_numbers,
697 c.legacy_sexagesimal,
698 c.lossless_u64_integers(),
699 ) {
700 crate::streaming::Scalar::Null => BorrowedValue::Null,
701 crate::streaming::Scalar::Bool(b) => BorrowedValue::Bool(b),
702 crate::streaming::Scalar::Int(i) => {
703 BorrowedValue::Number(crate::value::Number::Integer(i))
704 }
705 #[cfg(feature = "lossless-u64")]
706 crate::streaming::Scalar::Uint(u) => {
707 BorrowedValue::Number(crate::value::Number::Unsigned(u))
708 }
709 crate::streaming::Scalar::Float(f) => {
710 BorrowedValue::Number(crate::value::Number::Float(f))
711 }
712 crate::streaming::Scalar::Str(_) => BorrowedValue::String(value),
713 }
714 }
715
716 fn push_value(&mut self, value: BorrowedValue<'a>) -> Result<()> {
720 match self.stack.pop() {
721 Some(Frame::Sequence(mut seq, anchor)) => {
722 if seq.len() >= self.config.max_sequence_length {
723 return Err(Error::Budget(crate::BudgetBreach::MaxSequenceLength {
724 limit: self.config.max_sequence_length,
725 observed: seq.len() + 1,
726 }));
727 }
728 seq.push(value);
729 self.stack.push(Frame::Sequence(seq, anchor));
730 }
731 Some(Frame::MappingValue(mut map, key, anchor)) => {
732 self.insert_entry(&mut map, key, value)?;
733 self.stack.push(Frame::MappingKey(map, anchor));
734 }
735 Some(frame @ Frame::MappingKey(..)) => self.stack.push(frame),
737 None => self.result = Some(value),
738 }
739 Ok(())
740 }
741
742 fn insert_entry(
743 &self,
744 map: &mut IndexMap<Cow<'a, str>, BorrowedValue<'a>, FxBuildHasher>,
745 key: Cow<'a, str>,
746 value: BorrowedValue<'a>,
747 ) -> Result<()> {
748 if map.len() >= self.config.max_mapping_keys {
749 return Err(Error::Budget(crate::BudgetBreach::MaxMappingKeys {
750 limit: self.config.max_mapping_keys,
751 observed: map.len() + 1,
752 }));
753 }
754 match self.config.duplicate_key_policy {
755 InternalDuplicateKeyPolicy::First if map.contains_key(&key) => {}
756 InternalDuplicateKeyPolicy::Error if map.contains_key(&key) => {
757 return Err(Error::DuplicateKey(key.into_owned()));
758 }
759 _ => {
760 let _ = map.insert(key, value);
761 }
762 }
763 Ok(())
764 }
765
766 fn set_key(&mut self, key: Cow<'a, str>) {
768 if let Some(Frame::MappingKey(map, anchor)) = self.stack.pop() {
769 self.stack.push(Frame::MappingValue(map, key, anchor));
770 }
771 }
772
773 fn record_anchor(&mut self, anchor: Option<String>, value: &BorrowedValue<'a>) {
776 if let Some(name) = anchor {
777 let cost = borrowed_cost(value);
778 let _ = self.anchors.insert(name, (value.clone(), cost));
779 }
780 }
781
782 fn process(&mut self, event: Event<'a>) -> Result<()> {
783 self.meter.charge_event(&event, self.config)?;
784 match event {
785 Event::DocumentEnd => {
786 self.anchors.clear();
789 Ok(())
790 }
791 Event::Scalar {
792 value,
793 style,
794 anchor,
795 tag,
796 ..
797 } => self.on_scalar(value, style, anchor, tag.as_ref()),
798 Event::SequenceStart { anchor, .. } => {
799 self.open_collection()?;
800 self.stack
801 .push(Frame::Sequence(Vec::with_capacity(4), anchor));
802 Ok(())
803 }
804 Event::MappingStart { anchor, .. } => {
805 self.open_collection()?;
806 self.stack.push(Frame::MappingKey(
807 IndexMap::with_capacity_and_hasher(4, FxBuildHasher),
808 anchor,
809 ));
810 Ok(())
811 }
812 Event::SequenceEnd { .. } | Event::MappingEnd { .. } => self.close_collection(),
813 Event::Alias { anchor, .. } => self.on_alias(&anchor),
814 Event::StreamStart | Event::StreamEnd | Event::DocumentStart => Ok(()),
815 }
816 }
817
818 fn on_scalar(
819 &mut self,
820 value: Cow<'a, str>,
821 style: ScalarStyle,
822 anchor: Option<String>,
823 tag: Option<&(String, String)>,
824 ) -> Result<()> {
825 if let Some(Frame::MappingKey(..)) = self.stack.last() {
826 if style == ScalarStyle::Plain && value == MERGE_KEY {
827 self.charge_merge_key()?;
828 }
829 self.set_key(value);
830 return Ok(());
831 }
832 let resolved = self.resolve_scalar(value, style, tag);
833 self.record_anchor(anchor, &resolved);
834 self.push_value(resolved)
835 }
836
837 fn charge_merge_key(&mut self) -> Result<()> {
841 match self.config.merge_key_policy {
842 InternalMergeKeyPolicy::AsOrdinary => Ok(()),
843 InternalMergeKeyPolicy::Error => Err(Error::Custom(
844 "merge key `<<` rejected by MergeKeyPolicy::Error".to_owned(),
845 )),
846 InternalMergeKeyPolicy::Auto => self.meter.charge_merge_key(self.config),
847 }
848 }
849
850 fn open_collection(&mut self) -> Result<()> {
851 self.depth += 1;
852 if budget::depth_exceeded(self.depth, self.config.max_depth) {
853 return Err(Error::RecursionLimitExceeded { depth: self.depth });
854 }
855 Ok(())
856 }
857
858 fn close_collection(&mut self) -> Result<()> {
859 self.depth = self.depth.saturating_sub(1);
860 let (value, anchor) = match self.stack.pop() {
861 Some(Frame::Sequence(s, a)) => (BorrowedValue::Sequence(s), a),
862 Some(Frame::MappingKey(m, a) | Frame::MappingValue(m, _, a)) => {
863 (BorrowedValue::Mapping(m), a)
864 }
865 None => return Err(Error::Invalid("unexpected collection end".to_string())),
866 };
867 self.record_anchor(anchor, &value);
868 self.push_value(value)
869 }
870
871 fn on_alias(&mut self, anchor: &str) -> Result<()> {
872 self.meter.charge_alias(self.config)?;
873 let (referent, cost) = self
874 .anchors
875 .get(anchor)
876 .ok_or_else(|| Error::Parse(format!("unknown anchor: '{anchor}'")))?;
877 self.meter.charge_expansion(cost, self.depth, self.config)?;
878 let referent = referent.clone();
879 if let Some(Frame::MappingKey(..)) = self.stack.last() {
884 let key = alias_key(referent)?;
885 self.set_key(key);
886 return Ok(());
887 }
888 self.push_value(referent)
889 }
890}
891
892fn alias_key(referent: BorrowedValue<'_>) -> Result<Cow<'_, str>> {
896 match referent {
897 BorrowedValue::String(s) => Ok(s),
898 BorrowedValue::Bool(b) => Ok(Cow::Owned(b.to_string())),
899 BorrowedValue::Number(n) => Ok(Cow::Owned(n.to_string())),
900 BorrowedValue::Null => Ok(Cow::Borrowed("null")),
901 BorrowedValue::Sequence(_) | BorrowedValue::Mapping(_) => Err(Error::Invalid(
902 "alias resolved to a non-scalar cannot be used as a mapping key".to_string(),
903 )),
904 }
905}
906
907fn borrowed_cost(root: &BorrowedValue<'_>) -> AliasCost {
910 enum Step<'v, 'a> {
911 Visit(&'v BorrowedValue<'a>),
912 Close,
913 }
914 let mut tally = CostTally::default();
915 let mut pending = vec![Step::Visit(root)];
916 while let Some(step) = pending.pop() {
917 let value = match step {
918 Step::Visit(value) => value,
919 Step::Close => {
920 tally.close();
921 continue;
922 }
923 };
924 match value {
925 BorrowedValue::String(s) => tally.scalar(s.len()),
926 BorrowedValue::Sequence(items) => {
927 tally.open();
928 pending.push(Step::Close);
929 pending.extend(items.iter().map(Step::Visit));
930 }
931 BorrowedValue::Mapping(map) => {
932 tally.open();
933 pending.push(Step::Close);
934 for (key, item) in map {
935 tally.scalar(key.len());
936 pending.push(Step::Visit(item));
937 }
938 }
939 BorrowedValue::Null | BorrowedValue::Bool(_) | BorrowedValue::Number(_) => {
940 tally.scalar(0);
941 }
942 }
943 }
944 tally.finish()
945}
946
947fn is_core_string_tag(tag: Option<&(String, String)>) -> bool {
951 tag.is_some_and(|(handle, suffix)| {
952 suffix == "str" && (handle == "!!" || handle == "tag:yaml.org,2002:" || handle == "!")
953 })
954}