1use crate::scalar::ScalarValue;
4use std::collections::{BTreeMap, BTreeSet};
5use std::fmt;
6
7pub(crate) mod convert;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum YamlValue {
17 Scalar(ScalarValue),
19 Sequence(Vec<YamlValue>),
21 Mapping(BTreeMap<String, YamlValue>),
29 Set(BTreeSet<String>),
31 OrderedMapping(Vec<(String, YamlValue)>),
33 Pairs(Vec<(String, YamlValue)>),
35 Tagged(String, Box<YamlValue>),
41}
42
43impl YamlValue {
44 pub fn cast(
74 node: rowan::SyntaxNode<crate::yaml::Lang>,
75 ) -> Result<Option<Self>, crate::anchor_resolution::ResolveError> {
76 crate::as_yaml::YamlNode::from_syntax_peeled(node).map_or(Ok(None), |node| {
77 convert::resolved_value(node, None, &mut Vec::new())
78 })
79 }
80
81 pub fn scalar(value: impl Into<ScalarValue>) -> Self {
83 YamlValue::Scalar(value.into())
84 }
85
86 pub const fn sequence() -> Self {
88 YamlValue::Sequence(Vec::new())
89 }
90
91 pub const fn from_sequence(items: Vec<YamlValue>) -> Self {
93 YamlValue::Sequence(items)
94 }
95
96 pub const fn mapping() -> Self {
98 YamlValue::Mapping(BTreeMap::new())
99 }
100
101 pub fn parse_raw(yaml_str: &str) -> crate::YamlResult<Self> {
114 use std::str::FromStr;
115
116 let file = crate::YamlFile::from_str(yaml_str)?;
117 let mut docs = file.documents();
118 let first = docs.next();
119 if docs.next().is_some() {
120 return Err(crate::YamlError::InvalidOperation {
121 operation: "YamlValue::parse_raw".to_string(),
122 reason: "Input contains multiple YAML documents. Use YamlFile::from_str() and convert each with YamlValue::from_document().".to_string(),
123 });
124 }
125 match first {
126 Some(doc) => Ok(Self::from_document(&doc)?),
127 None => Ok(YamlValue::Scalar(ScalarValue::null())),
128 }
129 }
130
131 pub fn from_document(
144 doc: &crate::yaml::Document,
145 ) -> Result<Self, crate::anchor_resolution::ResolveError> {
146 use rowan::ast::AstNode;
147 let doc = crate::yaml::Document::cast(crate::nodes::immutable_copy(doc.syntax()))
148 .expect("a copy of a document is a document");
149 let registry = crate::anchor_resolution::AnchorRegistry::from_document(&doc);
150 for child in doc.syntax().children() {
151 let Some(node) = crate::as_yaml::YamlNode::from_syntax_peeled(child) else {
152 continue;
153 };
154 if let Some(value) = convert::resolved_value(node, Some(®istry), &mut Vec::new())? {
155 return Ok(value);
156 }
157 }
158 Ok(YamlValue::Scalar(ScalarValue::null()))
160 }
161
162 pub const fn from_mapping(map: BTreeMap<String, YamlValue>) -> Self {
164 YamlValue::Mapping(map)
165 }
166
167 pub const fn set() -> Self {
169 YamlValue::Set(BTreeSet::new())
170 }
171
172 pub const fn from_set(set: BTreeSet<String>) -> Self {
174 YamlValue::Set(set)
175 }
176
177 pub const fn ordered_mapping() -> Self {
179 YamlValue::OrderedMapping(Vec::new())
180 }
181
182 pub const fn from_ordered_mapping(pairs: Vec<(String, YamlValue)>) -> Self {
184 YamlValue::OrderedMapping(pairs)
185 }
186
187 pub const fn pairs() -> Self {
189 YamlValue::Pairs(Vec::new())
190 }
191
192 pub const fn from_pairs(pairs: Vec<(String, YamlValue)>) -> Self {
194 YamlValue::Pairs(pairs)
195 }
196
197 pub fn tagged(tag: impl Into<String>, value: YamlValue) -> Self {
210 match value {
211 YamlValue::Scalar(s) => YamlValue::Scalar(s.with_tag(tag)),
212 v => YamlValue::Tagged(tag.into(), Box::new(v)),
213 }
214 }
215
216 pub fn tag(&self) -> Option<&str> {
220 match self {
221 YamlValue::Scalar(s) => s.tag(),
222 YamlValue::Sequence(_) | YamlValue::Mapping(_) => None,
223 YamlValue::Set(_) => Some(crate::tag::SET),
224 YamlValue::OrderedMapping(_) => Some(crate::tag::OMAP),
225 YamlValue::Pairs(_) => Some(crate::tag::PAIRS),
226 YamlValue::Tagged(tag, _) => Some(tag),
227 }
228 }
229
230 #[inline]
234 pub fn is_scalar(&self) -> bool {
235 matches!(self, YamlValue::Scalar(_))
236 }
237
238 #[inline]
242 pub fn is_sequence(&self) -> bool {
243 matches!(self, YamlValue::Sequence(_))
244 }
245
246 #[inline]
250 pub fn is_mapping(&self) -> bool {
251 matches!(self, YamlValue::Mapping(_))
252 }
253
254 #[inline]
256 pub fn is_set(&self) -> bool {
257 matches!(self, YamlValue::Set(_))
258 }
259
260 #[inline]
262 pub fn is_ordered_mapping(&self) -> bool {
263 matches!(self, YamlValue::OrderedMapping(_))
264 }
265
266 #[inline]
268 pub fn is_pairs(&self) -> bool {
269 matches!(self, YamlValue::Pairs(_))
270 }
271
272 pub fn as_scalar(&self) -> Option<&ScalarValue> {
274 match self {
275 YamlValue::Scalar(s) => Some(s),
276 _ => None,
277 }
278 }
279
280 pub fn as_sequence(&self) -> Option<&[YamlValue]> {
282 match self {
283 YamlValue::Sequence(seq) => Some(seq),
284 _ => None,
285 }
286 }
287
288 pub fn as_sequence_mut(&mut self) -> Option<&mut Vec<YamlValue>> {
290 match self {
291 YamlValue::Sequence(seq) => Some(seq),
292 _ => None,
293 }
294 }
295
296 pub fn as_mapping(&self) -> Option<&BTreeMap<String, YamlValue>> {
298 match self {
299 YamlValue::Mapping(map) => Some(map),
300 _ => None,
301 }
302 }
303
304 pub fn as_mapping_mut(&mut self) -> Option<&mut BTreeMap<String, YamlValue>> {
306 match self {
307 YamlValue::Mapping(map) => Some(map),
308 _ => None,
309 }
310 }
311
312 pub fn as_set(&self) -> Option<&BTreeSet<String>> {
314 match self {
315 YamlValue::Set(set) => Some(set),
316 _ => None,
317 }
318 }
319
320 pub fn as_set_mut(&mut self) -> Option<&mut BTreeSet<String>> {
322 match self {
323 YamlValue::Set(set) => Some(set),
324 _ => None,
325 }
326 }
327
328 pub fn as_ordered_mapping(&self) -> Option<&[(String, YamlValue)]> {
330 match self {
331 YamlValue::OrderedMapping(pairs) => Some(pairs),
332 _ => None,
333 }
334 }
335
336 pub fn as_ordered_mapping_mut(&mut self) -> Option<&mut Vec<(String, YamlValue)>> {
338 match self {
339 YamlValue::OrderedMapping(pairs) => Some(pairs),
340 _ => None,
341 }
342 }
343
344 pub fn as_pairs(&self) -> Option<&[(String, YamlValue)]> {
346 match self {
347 YamlValue::Pairs(pairs) => Some(pairs),
348 _ => None,
349 }
350 }
351
352 pub fn as_pairs_mut(&mut self) -> Option<&mut Vec<(String, YamlValue)>> {
354 match self {
355 YamlValue::Pairs(pairs) => Some(pairs),
356 _ => None,
357 }
358 }
359
360 pub fn as_str(&self) -> Option<&str> {
366 match self {
367 YamlValue::Scalar(s) => Some(s.value()),
368 _ => None,
369 }
370 }
371
372 pub fn to_i64(&self) -> Option<i64> {
381 self.as_scalar().and_then(ScalarValue::to_i64)
382 }
383
384 pub fn to_f64(&self) -> Option<f64> {
392 self.as_scalar().and_then(ScalarValue::to_f64)
393 }
394
395 pub fn to_bool(&self) -> Option<bool> {
404 self.as_scalar().and_then(ScalarValue::to_bool)
405 }
406
407 pub fn to_yaml_string(&self, indent: usize) -> String {
416 let mut builder = rowan::GreenNodeBuilder::new();
417 builder.start_node(crate::lex::SyntaxKind::ROOT.into());
418 build_value(self, &mut builder, indent, None);
419 builder.finish_node();
420 let built = SyntaxNode::new_root(builder.finish()).text().to_string();
421 let mut text = if self.is_block_collection() {
422 " ".repeat(indent)
423 } else {
424 String::new()
425 };
426 text.push_str(built.strip_suffix('\n').unwrap_or(&built));
427 if text.ends_with('\n') {
428 text.push('\n');
429 }
430 text
431 }
432
433 fn is_block_collection(&self) -> bool {
436 match self {
437 YamlValue::Sequence(seq) => !seq.is_empty(),
438 YamlValue::Mapping(map) => !map.is_empty(),
439 _ => false,
440 }
441 }
442}
443
444impl fmt::Display for YamlValue {
445 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446 write!(f, "{}", self.to_yaml_string(0))
447 }
448}
449
450macro_rules! impl_from_scalar {
452 ($($t:ty),*) => {
453 $(
454 impl From<$t> for YamlValue {
455 fn from(value: $t) -> Self {
456 YamlValue::Scalar(ScalarValue::from(value))
457 }
458 }
459 )*
460 };
461}
462
463impl_from_scalar!(String, &str, i32, i64, f32, f64, bool);
464
465impl From<ScalarValue> for YamlValue {
466 fn from(value: ScalarValue) -> Self {
467 YamlValue::Scalar(value)
468 }
469}
470
471impl From<&YamlValue> for YamlValue {
472 fn from(value: &YamlValue) -> Self {
473 value.clone()
474 }
475}
476
477impl<T> From<Vec<T>> for YamlValue
478where
479 T: Into<YamlValue>,
480{
481 fn from(vec: Vec<T>) -> Self {
482 let values: Vec<_> = vec.into_iter().map(Into::into).collect();
483 YamlValue::Sequence(values)
484 }
485}
486
487impl<K, V> From<BTreeMap<K, V>> for YamlValue
488where
489 K: Into<String>,
490 V: Into<YamlValue>,
491{
492 fn from(map: BTreeMap<K, V>) -> Self {
493 YamlValue::Mapping(map.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
494 }
495}
496
497impl<T> From<BTreeSet<T>> for YamlValue
498where
499 T: Into<String>,
500{
501 fn from(set: BTreeSet<T>) -> Self {
502 YamlValue::Set(set.into_iter().map(Into::into).collect())
503 }
504}
505
506use crate::as_yaml::{
508 build_in, build_mapping, build_sequence, build_tagged, AsYaml, FlowQuoting, YamlKind,
509};
510use crate::yaml::SyntaxNode;
511
512fn build_tagged_pair_seq(
515 builder: &mut rowan::GreenNodeBuilder,
516 tag: &str,
517 pairs: &[(String, YamlValue)],
518 indent: usize,
519 flow: Option<FlowQuoting>,
520) -> bool {
521 let block = flow.is_none() && !pairs.is_empty();
522 build_tagged(builder, tag, block, indent, |builder| {
523 let items = pairs
524 .iter()
525 .map(|(k, v)| YamlValue::Mapping(BTreeMap::from([(k.clone(), v.clone())])));
526 build_sequence(builder, items, indent + 2, flow)
527 })
528}
529
530fn build_value(
533 value: &YamlValue,
534 builder: &mut rowan::GreenNodeBuilder,
535 indent: usize,
536 flow: Option<FlowQuoting>,
537) -> bool {
538 match value {
539 YamlValue::Scalar(s) => build_in(s, builder, indent, flow),
540 YamlValue::Mapping(map) => build_mapping(builder, map.iter(), indent, flow),
541 YamlValue::Sequence(seq) => build_sequence(builder, seq.iter(), indent, flow),
542 YamlValue::Set(set) => crate::as_yaml::build_set_content(
543 builder,
544 set.iter().map(|m| ScalarValue::string(m.as_str())),
545 indent,
546 flow,
547 ),
548 YamlValue::OrderedMapping(pairs) => {
549 build_tagged_pair_seq(builder, crate::tag::OMAP, pairs, indent, flow)
550 }
551 YamlValue::Pairs(pairs) => {
552 build_tagged_pair_seq(builder, crate::tag::PAIRS, pairs, indent, flow)
553 }
554 YamlValue::Tagged(tag, value) => {
555 let block = value.is_block_collection() && flow.is_none();
556 let content_indent = if block { indent + 2 } else { indent };
557 build_tagged(builder, tag, block, indent, |builder| {
558 build_value(value, builder, content_indent, flow)
559 })
560 }
561 }
562}
563
564impl AsYaml for YamlValue {
565 fn as_node(&self) -> Option<&SyntaxNode> {
566 None
567 }
568
569 fn kind(&self) -> YamlKind {
570 match self {
571 YamlValue::Scalar(_) => YamlKind::Scalar,
572 YamlValue::Sequence(_) => YamlKind::Sequence,
573 YamlValue::Mapping(_) => YamlKind::Mapping,
574 YamlValue::Set(_) => YamlKind::Tagged(std::borrow::Cow::Borrowed(crate::tag::SET)),
575 YamlValue::OrderedMapping(_) => {
576 YamlKind::Tagged(std::borrow::Cow::Borrowed(crate::tag::OMAP))
577 }
578 YamlValue::Pairs(_) => YamlKind::Tagged(std::borrow::Cow::Borrowed(crate::tag::PAIRS)),
579 YamlValue::Tagged(tag, _) => YamlKind::Tagged(std::borrow::Cow::Owned(tag.clone())),
580 }
581 }
582
583 fn build_content(
584 &self,
585 builder: &mut rowan::GreenNodeBuilder,
586 indent: usize,
587 flow_context: bool,
588 ) -> bool {
589 build_value(
590 self,
591 builder,
592 indent,
593 flow_context.then_some(FlowQuoting::Minimal),
594 )
595 }
596
597 fn build_flow_content(
598 &self,
599 builder: &mut rowan::GreenNodeBuilder,
600 indent: usize,
601 quoting: FlowQuoting,
602 ) -> bool {
603 build_value(self, builder, indent, Some(quoting))
604 }
605
606 fn is_inline(&self) -> bool {
607 match self {
608 YamlValue::Scalar(_) => true,
609 YamlValue::Mapping(map) => map.is_empty(),
610 YamlValue::Sequence(seq) => seq.is_empty(),
611 YamlValue::Set(_) => true,
616 YamlValue::OrderedMapping(_) => true,
617 YamlValue::Pairs(_) => true,
618 YamlValue::Tagged(_, _) => true,
619 }
620 }
621}
622
623impl AsYaml for BTreeMap<String, YamlValue> {
624 fn as_node(&self) -> Option<&SyntaxNode> {
625 None
626 }
627
628 fn kind(&self) -> YamlKind {
629 YamlKind::Mapping
630 }
631
632 fn build_content(
633 &self,
634 builder: &mut rowan::GreenNodeBuilder,
635 indent: usize,
636 flow_context: bool,
637 ) -> bool {
638 build_mapping(
639 builder,
640 self.iter(),
641 indent,
642 flow_context.then_some(FlowQuoting::Minimal),
643 )
644 }
645
646 fn build_flow_content(
647 &self,
648 builder: &mut rowan::GreenNodeBuilder,
649 indent: usize,
650 quoting: FlowQuoting,
651 ) -> bool {
652 build_mapping(builder, self.iter(), indent, Some(quoting))
653 }
654
655 fn is_inline(&self) -> bool {
656 self.is_empty()
657 }
658}
659
660impl AsYaml for Vec<YamlValue> {
661 fn as_node(&self) -> Option<&SyntaxNode> {
662 None
663 }
664
665 fn kind(&self) -> YamlKind {
666 YamlKind::Sequence
667 }
668
669 fn build_content(
670 &self,
671 builder: &mut rowan::GreenNodeBuilder,
672 indent: usize,
673 flow_context: bool,
674 ) -> bool {
675 build_sequence(
676 builder,
677 self.iter(),
678 indent,
679 flow_context.then_some(FlowQuoting::Minimal),
680 )
681 }
682
683 fn build_flow_content(
684 &self,
685 builder: &mut rowan::GreenNodeBuilder,
686 indent: usize,
687 quoting: FlowQuoting,
688 ) -> bool {
689 build_sequence(builder, self.iter(), indent, Some(quoting))
690 }
691
692 fn is_inline(&self) -> bool {
693 <&[YamlValue] as AsYaml>::is_inline(&self.as_slice())
694 }
695}
696
697impl AsYaml for &[YamlValue] {
698 fn as_node(&self) -> Option<&SyntaxNode> {
699 None
700 }
701
702 fn kind(&self) -> YamlKind {
703 YamlKind::Sequence
704 }
705
706 fn build_content(
707 &self,
708 builder: &mut rowan::GreenNodeBuilder,
709 indent: usize,
710 flow_context: bool,
711 ) -> bool {
712 build_sequence(
713 builder,
714 self.iter(),
715 indent,
716 flow_context.then_some(FlowQuoting::Minimal),
717 )
718 }
719
720 fn build_flow_content(
721 &self,
722 builder: &mut rowan::GreenNodeBuilder,
723 indent: usize,
724 quoting: FlowQuoting,
725 ) -> bool {
726 build_sequence(builder, self.iter(), indent, Some(quoting))
727 }
728
729 fn is_inline(&self) -> bool {
730 self.is_empty()
731 }
732}
733
734#[cfg(test)]
735mod tests {
736 use super::*;
737
738 use std::str::FromStr;
739
740 fn cast_root(yaml: &str) -> Result<Option<YamlValue>, crate::anchor_resolution::ResolveError> {
742 use rowan::ast::AstNode;
743 let doc = crate::Document::from_str(yaml).unwrap();
744 let content = doc
745 .syntax()
746 .children()
747 .find(|n| crate::as_yaml::YamlNode::from_syntax_peeled(n.clone()).is_some())
748 .unwrap();
749 YamlValue::cast(content)
750 }
751
752 #[test]
753 fn test_cast_alias_is_error() {
754 use crate::anchor_resolution::ResolveError;
755 for yaml in [
756 "x: &x 1\ny: [*x, 2]\n",
757 "x: &x 1\ny: *x\n",
758 "&x a: 1\n*x : 2\n",
759 "x: &x a\ny: !!set {? *x}\n",
760 "x: &x {a: 1}\ny: !!omap [*x]\n",
761 "x: &x 1\ny: !!pairs [a: *x]\n",
762 "x: &x 1\ny: !local [*x]\n",
763 "x: &x {a: 1}\ny: {<<: *x}\n",
764 ] {
765 assert_eq!(
766 cast_root(yaml),
767 Err(ResolveError::UnresolvedAlias {
768 alias: "x".to_string()
769 }),
770 "{yaml:?}"
771 );
772 }
773 assert_eq!(
774 ResolveError::UnresolvedAlias {
775 alias: "x".to_string()
776 }
777 .to_string(),
778 "alias \"x\" cannot be converted without resolving it; \
779 use get_resolved to resolve aliases"
780 );
781 }
782
783 #[test]
784 fn test_cast_key_collision_is_error() {
785 use crate::anchor_resolution::ResolveError;
786 assert_eq!(
787 cast_root("1: a\n'1': b\n"),
788 Err(ResolveError::KeyCollision {
789 first: "1".to_string(),
790 second: "'1'".to_string()
791 })
792 );
793 }
794
795 #[test]
796 fn test_cast_invalid_special_collections() {
797 use crate::anchor_resolution::ResolveError;
798 assert_eq!(
799 cast_root("!!set {? [a], ? b}\n"),
800 Err(ResolveError::InvalidSetMember {
801 member: "[a]".to_string()
802 })
803 );
804 assert_eq!(
805 cast_root("!!set [a, b]\n"),
806 Err(ResolveError::InvalidSetContent {
807 content: "[a, b]".to_string()
808 })
809 );
810 assert_eq!(
811 cast_root("!!omap {a: 1}\n"),
812 Err(ResolveError::InvalidPairsContent {
813 tag: "!!omap".to_string(),
814 content: "{a: 1}".to_string()
815 })
816 );
817 for (yaml, expected) in [
819 (
820 "!!set\n",
821 ResolveError::InvalidSetContent {
822 content: String::new(),
823 },
824 ),
825 (
826 "!!omap\n",
827 ResolveError::InvalidPairsContent {
828 tag: "!!omap".to_string(),
829 content: String::new(),
830 },
831 ),
832 (
833 "- !!pairs\n- a: 1\n",
834 ResolveError::InvalidPairsContent {
835 tag: "!!pairs".to_string(),
836 content: String::new(),
837 },
838 ),
839 ] {
840 let doc = crate::Document::from_str(yaml).unwrap();
841 assert_eq!(YamlValue::from_document(&doc), Err(expected), "{yaml:?}");
842 }
843 for (yaml, tag, item) in [
844 ("!!omap [a, d: 3]\n", "!!omap", "a"),
845 ("!!pairs [{b: 1, c: 2}]\n", "!!pairs", "{b: 1, c: 2}"),
846 ] {
847 assert_eq!(
848 cast_root(yaml),
849 Err(ResolveError::InvalidPairsItem {
850 tag: tag.to_string(),
851 item: item.to_string()
852 }),
853 "{yaml:?}"
854 );
855 }
856 }
857
858 #[test]
859 fn test_cast_duplicate_key_is_error() {
860 use crate::anchor_resolution::ResolveError;
861 let expected = ResolveError::DuplicateKey {
862 first: "a".to_string(),
863 second: "a".to_string(),
864 };
865 let yaml = "a: 1\nb: 2\na: 3\n";
866 assert_eq!(cast_root(yaml), Err(expected.clone()));
867 let doc = crate::Document::from_str(yaml).unwrap();
868 assert_eq!(YamlValue::from_document(&doc), Err(expected.clone()));
869 match YamlValue::parse_raw(yaml) {
870 Err(crate::YamlError::Resolve(err)) => assert_eq!(err, expected),
871 other => panic!("unexpected result: {other:?}"),
872 }
873 assert_eq!(
876 cast_root("<<: {a: 1}\n<<: {b: 2}\n"),
877 Err(ResolveError::DuplicateKey {
878 first: "<<".to_string(),
879 second: "<<".to_string(),
880 })
881 );
882 }
883
884 #[test]
885 fn test_cast_keeps_merge_key() {
886 assert_eq!(
887 cast_root("<<: {a: 1}\nb: 2\n"),
888 Ok(Some(YamlValue::Mapping(BTreeMap::from([
889 (
890 "<<".to_string(),
891 YamlValue::Mapping(BTreeMap::from([("a".to_string(), YamlValue::from(1))]))
892 ),
893 ("b".to_string(), YamlValue::from(2)),
894 ]))))
895 );
896 }
897
898 #[test]
899 fn test_tagged_constructor() {
900 let v = YamlValue::tagged("!!str", YamlValue::from(1));
901 assert_eq!(v, YamlValue::Scalar(ScalarValue::from(1).with_tag("!!str")));
902 assert_eq!(v.tag(), Some("!!str"));
903 assert_eq!(YamlValue::from(1).tag(), None);
904 assert_eq!(YamlValue::set().tag(), Some("!!set"));
905 }
906
907 #[test]
908 fn test_predicates_and_accessors() {
909 let scalar = YamlValue::from("s");
910 let seq = YamlValue::Sequence(vec![YamlValue::from("a")]);
911 let map = {
912 let mut m = BTreeMap::new();
913 m.insert("k".to_string(), YamlValue::from("v"));
914 YamlValue::Mapping(m)
915 };
916 let set = YamlValue::from_set(BTreeSet::from(["x".to_string()]));
917 let omap = YamlValue::from_ordered_mapping(vec![("a".to_string(), YamlValue::from("b"))]);
918 let pairs = YamlValue::from_pairs(vec![("a".to_string(), YamlValue::from("b"))]);
919
920 assert!(scalar.is_scalar());
922 assert!(!seq.is_scalar());
923
924 assert!(seq.is_sequence());
925 assert!(!scalar.is_sequence());
926
927 assert!(map.is_mapping());
928 assert!(!seq.is_mapping());
929
930 assert!(set.is_set());
931 assert!(!map.is_set());
932
933 assert!(omap.is_ordered_mapping());
934 assert!(!map.is_ordered_mapping());
935
936 assert!(pairs.is_pairs());
937 assert!(!omap.is_pairs());
938
939 assert!(scalar.as_scalar().is_some());
941 assert!(seq.as_scalar().is_none());
942
943 assert_eq!(seq.as_sequence(), Some([YamlValue::from("a")].as_slice()));
944 assert_eq!(scalar.as_sequence(), None);
945
946 assert_eq!(map.as_mapping().map(|m| m.len()), Some(1));
947 assert_eq!(scalar.as_mapping(), None);
948
949 assert_eq!(set.as_set().map(|s| s.len()), Some(1));
950 assert_eq!(map.as_set(), None);
951
952 assert_eq!(omap.as_ordered_mapping().map(|p| p.len()), Some(1));
953 assert_eq!(map.as_ordered_mapping(), None);
954
955 assert_eq!(pairs.as_pairs().map(|p| p.len()), Some(1));
956 assert_eq!(omap.as_pairs(), None);
957 }
958
959 #[test]
960 fn test_accessors_mut() {
961 let mut seq = YamlValue::Sequence(vec![YamlValue::from("a")]);
962 seq.as_sequence_mut().unwrap().push(YamlValue::from("b"));
963 assert_eq!(seq.as_sequence().map(|s| s.len()), Some(2));
964 assert!(YamlValue::from("x").as_sequence_mut().is_none());
965
966 let mut map = YamlValue::mapping();
967 map.as_mapping_mut()
968 .unwrap()
969 .insert("k".to_string(), YamlValue::from("v"));
970 assert_eq!(map.as_mapping().map(|m| m.len()), Some(1));
971 assert!(YamlValue::from("x").as_mapping_mut().is_none());
972
973 let mut set = YamlValue::set();
974 set.as_set_mut().unwrap().insert("a".to_string());
975 assert_eq!(set.as_set().map(|s| s.len()), Some(1));
976 assert!(YamlValue::from("x").as_set_mut().is_none());
977
978 let mut omap = YamlValue::ordered_mapping();
979 omap.as_ordered_mapping_mut()
980 .unwrap()
981 .push(("a".to_string(), YamlValue::from("b")));
982 assert_eq!(omap.as_ordered_mapping().map(|p| p.len()), Some(1));
983 assert!(YamlValue::from("x").as_ordered_mapping_mut().is_none());
984
985 let mut pairs = YamlValue::pairs();
986 pairs
987 .as_pairs_mut()
988 .unwrap()
989 .push(("a".to_string(), YamlValue::from("b")));
990 assert_eq!(pairs.as_pairs().map(|p| p.len()), Some(1));
991 assert!(YamlValue::from("x").as_pairs_mut().is_none());
992 }
993
994 #[test]
995 fn test_as_str() {
996 let string_val = YamlValue::from("hello");
998 assert_eq!(string_val.as_str(), Some("hello"));
999
1000 let int_val = YamlValue::from(42);
1001 assert_eq!(int_val.as_str(), Some("42"));
1002
1003 let float_val = YamlValue::from(1.5);
1004 assert_eq!(float_val.as_str(), Some("1.5"));
1005
1006 let bool_val = YamlValue::from(true);
1007 assert_eq!(bool_val.as_str(), Some("true"));
1008
1009 let mapping = YamlValue::mapping();
1011 assert_eq!(mapping.as_str(), None);
1012
1013 let sequence = YamlValue::sequence();
1014 assert_eq!(sequence.as_str(), None);
1015
1016 let set = YamlValue::set();
1017 assert_eq!(set.as_str(), None);
1018 }
1019
1020 #[test]
1021 fn test_to_i64() {
1022 use crate::scalar::ScalarValue;
1023
1024 let int_val = YamlValue::Scalar(ScalarValue::parse("42"));
1026 assert_eq!(int_val.to_i64(), Some(42));
1027
1028 let neg_int = YamlValue::Scalar(ScalarValue::parse("-123"));
1029 assert_eq!(neg_int.to_i64(), Some(-123));
1030
1031 let hex_val = YamlValue::Scalar(ScalarValue::parse("0x2A"));
1033 assert_eq!(hex_val.to_i64(), Some(42));
1034
1035 let bin_val = YamlValue::Scalar(ScalarValue::parse("0b101010"));
1037 assert_eq!(bin_val.to_i64(), None);
1038
1039 let oct_val = YamlValue::Scalar(ScalarValue::parse("0o52"));
1041 assert_eq!(oct_val.to_i64(), Some(42));
1042
1043 let string_val = YamlValue::from("42");
1045 assert_eq!(string_val.to_i64(), None);
1046
1047 let bool_val = YamlValue::Scalar(ScalarValue::parse("true"));
1049 assert_eq!(bool_val.to_i64(), None);
1050
1051 let no_val = YamlValue::Scalar(ScalarValue::parse("no"));
1052 assert_eq!(no_val.to_i64(), None);
1053
1054 let float_val = YamlValue::Scalar(ScalarValue::parse("3.14"));
1056 assert_eq!(float_val.to_i64(), None);
1057
1058 let mapping = YamlValue::mapping();
1060 assert_eq!(mapping.to_i64(), None);
1061
1062 let sequence = YamlValue::sequence();
1063 assert_eq!(sequence.to_i64(), None);
1064
1065 let random_string = YamlValue::from("blah");
1067 assert_eq!(random_string.to_i64(), None);
1068
1069 let large_int = YamlValue::Scalar(ScalarValue::parse("23432"));
1071 assert_eq!(large_int.to_i64(), Some(23432));
1072
1073 let zero = YamlValue::Scalar(ScalarValue::parse("0"));
1075 assert_eq!(zero.to_i64(), Some(0));
1076
1077 let one = YamlValue::Scalar(ScalarValue::parse("1"));
1078 assert_eq!(one.to_i64(), Some(1));
1079
1080 let quoted_one = YamlValue::from("1");
1082 assert_eq!(quoted_one.to_i64(), None);
1083
1084 let quoted_zero = YamlValue::from("0");
1085 assert_eq!(quoted_zero.to_i64(), None);
1086 }
1087
1088 #[test]
1089 fn test_to_f64() {
1090 use crate::scalar::ScalarValue;
1091
1092 let float_val = YamlValue::Scalar(ScalarValue::parse("1.5"));
1094 assert_eq!(float_val.to_f64(), Some(1.5));
1095
1096 let neg_float = YamlValue::Scalar(ScalarValue::parse("-2.5"));
1097 assert_eq!(neg_float.to_f64(), Some(-2.5));
1098
1099 let sci_val = YamlValue::Scalar(ScalarValue::parse("1.23e10"));
1101 assert_eq!(sci_val.to_f64(), Some(1.23e10));
1102
1103 let string_val = YamlValue::from("3.14");
1105 assert_eq!(string_val.to_f64(), None);
1106
1107 let int_val = YamlValue::Scalar(ScalarValue::parse("42"));
1109 assert_eq!(int_val.to_f64(), None);
1110
1111 let bool_val = YamlValue::Scalar(ScalarValue::parse("true"));
1113 assert_eq!(bool_val.to_f64(), None);
1114
1115 let mapping = YamlValue::mapping();
1117 assert_eq!(mapping.to_f64(), None);
1118
1119 let sequence = YamlValue::sequence();
1120 assert_eq!(sequence.to_f64(), None);
1121
1122 let random_string = YamlValue::from("blah");
1124 assert_eq!(random_string.to_f64(), None);
1125 }
1126
1127 #[test]
1128 fn test_to_bool() {
1129 use crate::scalar::ScalarValue;
1130
1131 let true_val = YamlValue::Scalar(ScalarValue::parse("true"));
1133 assert_eq!(true_val.to_bool(), Some(true));
1134
1135 let false_val = YamlValue::Scalar(ScalarValue::parse("false"));
1136 assert_eq!(false_val.to_bool(), Some(false));
1137
1138 for text in ["yes", "no", "on", "off", "Yes", "tRUE"] {
1140 let value = YamlValue::Scalar(ScalarValue::parse(text));
1141 assert_eq!(value.to_bool(), None, "{text}");
1142 }
1143
1144 let true_upper = YamlValue::Scalar(ScalarValue::parse("TRUE"));
1145 assert_eq!(true_upper.to_bool(), Some(true));
1146
1147 let false_title = YamlValue::Scalar(ScalarValue::parse("False"));
1148 assert_eq!(false_title.to_bool(), Some(false));
1149
1150 let quoted_true = YamlValue::from("true");
1152 assert_eq!(quoted_true.to_bool(), None);
1153
1154 let quoted_false = YamlValue::from("false");
1155 assert_eq!(quoted_false.to_bool(), None);
1156
1157 let quoted_yes = YamlValue::from("yes");
1158 assert_eq!(quoted_yes.to_bool(), None);
1159
1160 let quoted_no = YamlValue::from("no");
1161 assert_eq!(quoted_no.to_bool(), None);
1162
1163 let zero = YamlValue::Scalar(ScalarValue::parse("0"));
1165 assert_eq!(zero.to_bool(), None);
1166
1167 let one = YamlValue::Scalar(ScalarValue::parse("1"));
1168 assert_eq!(one.to_bool(), None);
1169
1170 let large_int = YamlValue::Scalar(ScalarValue::parse("23432"));
1171 assert_eq!(large_int.to_bool(), None);
1172
1173 let quoted_one = YamlValue::from("1");
1175 assert_eq!(quoted_one.to_bool(), None);
1176
1177 let quoted_zero = YamlValue::from("0");
1178 assert_eq!(quoted_zero.to_bool(), None);
1179
1180 let random_string = YamlValue::from("blah");
1182 assert_eq!(random_string.to_bool(), None);
1183
1184 let mapping = YamlValue::mapping();
1186 assert_eq!(mapping.to_bool(), None);
1187
1188 let sequence = YamlValue::sequence();
1189 assert_eq!(sequence.to_bool(), None);
1190
1191 let float_val = YamlValue::Scalar(ScalarValue::parse("3.14"));
1193 assert_eq!(float_val.to_bool(), None);
1194 }
1195
1196 #[test]
1197 fn test_tagged_alias_to_tagged_alias() {
1198 use std::str::FromStr;
1199 let doc = crate::Document::from_str("b: &b 1\na: &a !u *b\nk: !t *a\n").unwrap();
1200 let value = YamlValue::from_document(&doc).unwrap();
1201 let YamlValue::Mapping(map) = value else {
1202 panic!("expected a mapping");
1203 };
1204 assert_eq!(
1205 map["k"],
1206 YamlValue::Scalar(ScalarValue::parse("1").with_resolved_tag("!t".to_string()))
1207 );
1208 assert_eq!(map["k"].tag(), Some("!t"));
1209 }
1210
1211 #[test]
1212 fn test_omap_items_tagged_map() {
1213 use std::str::FromStr;
1214 let doc = crate::Document::from_str("o: !!omap [!!map {a: 1}, {b: 2}]\n").unwrap();
1215 let expected = YamlValue::OrderedMapping(vec![
1216 ("a".to_string(), YamlValue::from(1i64)),
1217 ("b".to_string(), YamlValue::from(2i64)),
1218 ]);
1219 let o = doc.as_mapping().unwrap().get("o").unwrap();
1220 assert_eq!(
1221 YamlValue::cast(o.syntax().clone()),
1222 Ok(Some(expected.clone()))
1223 );
1224 assert_eq!(
1225 YamlValue::from_document(&doc),
1226 Ok(YamlValue::from_mapping(BTreeMap::from([(
1227 "o".to_string(),
1228 expected
1229 )])))
1230 );
1231
1232 let doc = crate::Document::from_str("o: !!omap [!!map [a]]\n").unwrap();
1233 assert_eq!(
1234 YamlValue::from_document(&doc),
1235 Err(crate::anchor_resolution::ResolveError::InvalidPairsItem {
1236 tag: "!!omap".to_string(),
1237 item: "!!map [a]".to_string(),
1238 })
1239 );
1240 }
1241}