1use super::{Lang, Scalar, Sequence, SyntaxNode};
2use crate::as_yaml::{AsYaml, YamlKind};
3use crate::lex::SyntaxKind;
4use crate::yaml::{
5 add_newline_token, add_node_children_to, dump_cst_to_string, ends_with_newline, Document,
6 ValueNode,
7};
8use rowan::ast::AstNode;
9use rowan::GreenNodeBuilder;
10
11ast_node!(
12 MappingEntry,
13 MAPPING_ENTRY,
14 "A key-value pair in a YAML mapping"
15);
16
17impl MappingEntry {
18 #[cfg(test)]
20 pub(crate) fn syntax(&self) -> &SyntaxNode {
21 &self.0
22 }
23
24 pub(crate) fn key(&self) -> Option<SyntaxNode> {
32 self.0.children().find(|n| n.kind() == SyntaxKind::KEY)
33 }
34
35 pub fn key_matches(&self, key: impl crate::AsYaml) -> bool {
41 self.key().is_some_and(|k| key_content_matches(&k, key))
42 }
43
44 pub(crate) fn value(&self) -> Option<SyntaxNode> {
50 self.0.children().find(|n| n.kind() == SyntaxKind::VALUE)
51 }
52
53 pub fn key_node(&self) -> Option<crate::as_yaml::YamlNode> {
57 self.key()
58 .and_then(|k| k.children().next())
59 .and_then(crate::as_yaml::YamlNode::from_syntax)
60 }
61
62 pub fn value_node(&self) -> Option<crate::as_yaml::YamlNode> {
66 self.value()
67 .and_then(|v| v.children().next())
68 .and_then(crate::as_yaml::YamlNode::from_syntax)
69 }
70
71 pub fn new(
77 key: impl crate::AsYaml,
78 value: impl crate::AsYaml,
79 flow_context: bool,
80 use_explicit_key: bool,
81 ) -> Self {
82 let mut builder = GreenNodeBuilder::new();
83 builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
84
85 if use_explicit_key {
86 builder.token(SyntaxKind::QUESTION.into(), "?");
88 builder.token(SyntaxKind::WHITESPACE.into(), " ");
89 }
90
91 builder.start_node(SyntaxKind::KEY.into());
93 let key_has_newline = key.build_content(&mut builder, 0, false);
94 debug_assert!(!key_has_newline, "Keys should not end with newlines");
95 builder.finish_node();
96
97 if use_explicit_key {
98 builder.token(SyntaxKind::NEWLINE.into(), "\n");
100 }
101
102 builder.token(SyntaxKind::COLON.into(), ":");
103
104 builder.start_node(SyntaxKind::VALUE.into());
108 let value_ends_with_newline = match (value.is_inline(), value.kind()) {
109 (true, _) => {
111 builder.token(SyntaxKind::WHITESPACE.into(), " ");
112 value.build_content(&mut builder, 0, flow_context)
115 }
116 (false, crate::as_yaml::YamlKind::Mapping)
119 | (false, crate::as_yaml::YamlKind::Sequence) => {
120 builder.token(SyntaxKind::NEWLINE.into(), "\n");
121 value.build_content(&mut builder, 0, flow_context)
122 }
123 (false, _) => {
125 builder.token(SyntaxKind::NEWLINE.into(), "\n");
126 builder.token(SyntaxKind::INDENT.into(), " ");
127 value.build_content(&mut builder, 2, flow_context)
128 }
129 };
130 builder.finish_node(); if !value_ends_with_newline {
134 builder.token(SyntaxKind::NEWLINE.into(), "\n");
135 }
136
137 builder.finish_node(); MappingEntry(SyntaxNode::new_root_mut(builder.finish()))
139 }
140
141 pub fn set_value(&self, new_value: impl crate::AsYaml, flow_context: bool) {
143 let mut value_builder = GreenNodeBuilder::new();
145 value_builder.start_node(SyntaxKind::VALUE.into());
146 new_value.build_content(&mut value_builder, 0, flow_context);
147
148 for child in self.0.children_with_tokens() {
150 if let Some(node) = child.as_node() {
151 if node.kind() == SyntaxKind::VALUE {
152 let mut found_content = false;
157 for val_child in node.children_with_tokens() {
158 match val_child.kind() {
159 SyntaxKind::WHITESPACE | SyntaxKind::COMMENT if found_content => {
160 if let Some(tok) = val_child.as_token() {
162 value_builder.token(tok.kind().into(), tok.text());
163 }
164 }
165 SyntaxKind::WHITESPACE | SyntaxKind::COMMENT => {
166 }
168 _ => {
169 found_content = true;
170 }
171 }
172 }
173 break;
174 }
175 }
176 }
177
178 value_builder.finish_node();
179 let new_value_node = SyntaxNode::new_root_mut(value_builder.finish());
180
181 for (i, child) in self.0.children_with_tokens().enumerate() {
183 if let Some(node) = child.as_node() {
184 if node.kind() == SyntaxKind::VALUE {
185 self.0
186 .splice_children(i..i + 1, vec![new_value_node.into()]);
187 return;
188 }
189 }
190 }
191
192 let mut builder = GreenNodeBuilder::new();
196 builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
197
198 let mut value_inserted = false;
199 for child in self.0.children_with_tokens() {
200 match child {
201 rowan::NodeOrToken::Node(n) => {
202 builder.start_node(n.kind().into());
204 add_node_children_to(&mut builder, &n);
205 builder.finish_node();
206 }
207 rowan::NodeOrToken::Token(t) => {
208 builder.token(t.kind().into(), t.text());
210 if t.kind() == SyntaxKind::COLON && !value_inserted {
212 builder.token(SyntaxKind::WHITESPACE.into(), " ");
213 add_node_children_to(&mut builder, &new_value_node);
214 value_inserted = true;
215 }
216 }
217 }
218 }
219
220 builder.finish_node();
221 let new_green = builder.finish();
222 let new_entry = SyntaxNode::new_root_mut(new_green);
223
224 let new_children: Vec<_> = new_entry.children_with_tokens().collect();
227 let child_count = self.0.children_with_tokens().count();
228 self.0.splice_children(0..child_count, new_children);
229 }
230
231 pub fn discard(self) {
237 self.0.detach();
238 }
239
240 pub fn remove(self) {
249 self.discard();
250 }
251}
252
253ast_node!(Mapping, MAPPING, "A YAML mapping (key-value pairs)");
254
255impl Mapping {
256 pub fn dump_cst(&self) -> String {
261 dump_cst_to_string(&self.0, 0)
262 }
263
264 pub fn keys(&self) -> impl Iterator<Item = crate::as_yaml::YamlNode> + '_ {
276 self.pairs().filter_map(|(k, _)| {
277 k.children()
278 .next()
279 .and_then(crate::as_yaml::YamlNode::from_syntax)
280 })
281 }
282
283 pub(crate) fn pairs(&self) -> impl Iterator<Item = (SyntaxNode, SyntaxNode)> + '_ {
295 self.0
296 .children()
297 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
298 .filter_map(|entry| {
299 let key = entry.children().find(|n| n.kind() == SyntaxKind::KEY)?;
300 let value = entry.children().find(|n| n.kind() == SyntaxKind::VALUE)?;
301 Some((key, value))
302 })
303 }
304
305 pub fn get(&self, key: impl crate::AsYaml) -> Option<crate::as_yaml::YamlNode> {
309 self.get_node(key)
310 .and_then(crate::as_yaml::YamlNode::from_syntax)
311 }
312
313 pub(crate) fn get_node(&self, key: impl crate::AsYaml) -> Option<SyntaxNode> {
319 self.find_entry_by_key(key)
320 .and_then(|entry| entry.value())
321 .and_then(|value_node| {
322 value_node.children().next()
324 })
325 }
326
327 pub fn get_mapping(&self, key: impl crate::AsYaml) -> Option<Mapping> {
331 self.get(key).and_then(|n| n.as_mapping().cloned())
332 }
333
334 pub fn modify_mapping<F>(&self, key: impl crate::AsYaml, f: F) -> bool
344 where
345 F: FnOnce(&Mapping),
346 {
347 let children: Vec<_> = self.0.children_with_tokens().collect();
349 for (i, child) in children.iter().enumerate() {
350 if let Some(node) = child.as_node() {
351 if node.kind() == SyntaxKind::MAPPING_ENTRY {
352 if let Some(key_node) = node.children().find(|n| n.kind() == SyntaxKind::KEY) {
353 if key_content_matches(&key_node, &key) {
354 if let Some(value_node) =
356 node.children().find(|n| n.kind() == SyntaxKind::VALUE)
357 {
358 if let Some(mapping_node) = value_node
360 .children()
361 .find(|n| n.kind() == SyntaxKind::MAPPING)
362 {
363 let mapping = Mapping(mapping_node);
365 f(&mapping);
366
367 let entry_children: Vec<_> =
369 node.children_with_tokens().collect();
370 let mut builder = GreenNodeBuilder::new();
371 builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
372
373 for entry_child in entry_children {
374 match entry_child {
375 rowan::NodeOrToken::Node(n)
376 if n.kind() == SyntaxKind::VALUE =>
377 {
378 builder.start_node(SyntaxKind::VALUE.into());
380
381 for value_child in n.children_with_tokens() {
383 match value_child {
384 rowan::NodeOrToken::Node(child_node)
385 if child_node.kind()
386 == SyntaxKind::MAPPING =>
387 {
388 crate::yaml::copy_node_to_builder(
390 &mut builder,
391 &mapping.0,
392 );
393 }
394 rowan::NodeOrToken::Node(child_node) => {
395 crate::yaml::copy_node_to_builder(
397 &mut builder,
398 &child_node,
399 );
400 }
401 rowan::NodeOrToken::Token(token) => {
402 builder.token(
404 token.kind().into(),
405 token.text(),
406 );
407 }
408 }
409 }
410
411 builder.finish_node(); }
413 rowan::NodeOrToken::Node(n) => {
414 crate::yaml::copy_node_to_builder(&mut builder, &n);
415 }
416 rowan::NodeOrToken::Token(t) => {
417 builder.token(t.kind().into(), t.text());
418 }
419 }
420 }
421
422 builder.finish_node(); let new_entry = SyntaxNode::new_root_mut(builder.finish());
424 self.0.splice_children(i..i + 1, vec![new_entry.into()]);
425 return true;
426 }
427 }
428 }
429 }
430 }
431 }
432 }
433 false
434 }
435
436 pub fn get_sequence(&self, key: impl crate::AsYaml) -> Option<Sequence> {
440 self.get(key).and_then(|n| n.as_sequence().cloned())
441 }
442
443 pub fn contains_key(&self, key: impl crate::AsYaml) -> bool {
445 self.find_entry_by_key(key).is_some()
446 }
447
448 pub(crate) fn key_nodes(&self) -> impl Iterator<Item = SyntaxNode> + '_ {
455 self.pairs().map(|(k, _)| k)
456 }
457
458 pub fn is_empty(&self) -> bool {
460 self.pairs().next().is_none()
461 }
462
463 pub fn len(&self) -> usize {
465 self.pairs().count()
466 }
467
468 pub fn values(&self) -> impl Iterator<Item = crate::as_yaml::YamlNode> + '_ {
475 self.pairs().filter_map(|(_, value_node)| {
476 value_node
478 .children()
479 .next()
480 .and_then(crate::as_yaml::YamlNode::from_syntax)
481 })
482 }
483
484 pub fn iter(
490 &self,
491 ) -> impl Iterator<Item = (crate::as_yaml::YamlNode, crate::as_yaml::YamlNode)> + '_ {
492 self.pairs().filter_map(|(key_node, value_node)| {
493 let key = key_node
495 .children()
496 .next()
497 .and_then(crate::as_yaml::YamlNode::from_syntax)?;
498 let value = value_node
499 .children()
500 .next()
501 .and_then(crate::as_yaml::YamlNode::from_syntax)?;
502 Some((key, value))
503 })
504 }
505
506 pub fn new() -> Self {
508 let mut builder = GreenNodeBuilder::new();
509 builder.start_node(SyntaxKind::MAPPING.into());
510 builder.finish_node();
511 Mapping(SyntaxNode::new_root_mut(builder.finish()))
512 }
513
514 pub fn reorder_fields<I, K>(&self, order: I)
519 where
520 I: IntoIterator<Item = K>,
521 K: crate::AsYaml,
522 {
523 let order_keys: Vec<K> = order.into_iter().collect();
524
525 let entry_nodes: Vec<SyntaxNode> = self
527 .0
528 .children()
529 .filter(|child| child.kind() == SyntaxKind::MAPPING_ENTRY)
530 .collect();
531
532 let mut ordered_entries: Vec<SyntaxNode> = Vec::new();
534 let mut remaining_entries: Vec<SyntaxNode> = entry_nodes;
535
536 for order_key in &order_keys {
537 if let Some(pos) = remaining_entries.iter().position(|entry| {
538 entry
539 .children()
540 .find(|n| n.kind() == SyntaxKind::KEY)
541 .map(|k| key_content_matches(&k, order_key))
542 .unwrap_or(false)
543 }) {
544 ordered_entries.push(remaining_entries.remove(pos));
545 }
546 }
547
548 let new_children: Vec<_> = ordered_entries
549 .into_iter()
550 .chain(remaining_entries)
551 .map(|node| node.into())
552 .collect();
553
554 let children_count = self.0.children_with_tokens().count();
556 self.0.splice_children(0..children_count, new_children);
557 }
558}
559
560fn key_content_matches(key_node: &SyntaxNode, key: impl crate::AsYaml) -> bool {
564 match crate::as_yaml::YamlNode::from_syntax_peeled(key_node.clone()) {
565 Some(node) => crate::as_yaml::yaml_eq(&node, &key),
566 None => false,
567 }
568}
569
570impl Mapping {
571 pub fn is_flow_style(&self) -> bool {
576 self.0.children_with_tokens().any(|child| {
578 child
579 .as_token()
580 .map(|token| token.kind() == SyntaxKind::LEFT_BRACE)
581 .unwrap_or(false)
582 })
583 }
584
585 pub fn find_entry_by_key(&self, key: impl crate::AsYaml) -> Option<MappingEntry> {
590 self.0
591 .children()
592 .filter_map(MappingEntry::cast)
593 .find(|entry| entry.key().is_some_and(|k| key_content_matches(&k, &key)))
594 }
595
596 pub fn find_all_entries_by_key<'a>(
627 &'a self,
628 key: impl crate::AsYaml + 'a,
629 ) -> impl Iterator<Item = MappingEntry> + 'a {
630 self.0
631 .children()
632 .filter_map(MappingEntry::cast)
633 .filter(move |entry| entry.key().is_some_and(|k| key_content_matches(&k, &key)))
634 }
635
636 pub fn entries(&self) -> impl Iterator<Item = MappingEntry> {
642 self.0.children().filter_map(MappingEntry::cast)
643 }
644
645 pub fn find_entry_index_by_key(&self, key: impl crate::AsYaml) -> Option<usize> {
651 self.0
653 .children_with_tokens()
654 .enumerate()
655 .find_map(|(i, child)| {
656 let node = child.as_node()?;
657 if node.kind() != SyntaxKind::MAPPING_ENTRY {
658 return None;
659 }
660 let entry = MappingEntry::cast(node.clone())?;
661 if entry.key().is_some_and(|k| key_content_matches(&k, &key)) {
662 Some(i)
663 } else {
664 None
665 }
666 })
667 }
668
669 pub fn set(&self, key: impl crate::AsYaml, value: impl crate::AsYaml) {
679 self.set_as_yaml(key, value);
680 }
681
682 fn uses_explicit_keys(&self) -> bool {
684 for child in self.0.children() {
687 if child.kind() == SyntaxKind::MAPPING_ENTRY {
688 if child.children_with_tokens().any(|t| {
690 t.as_token()
691 .is_some_and(|tok| tok.kind() == SyntaxKind::QUESTION)
692 }) {
693 return true;
694 }
695 }
696 }
697 false
698 }
699
700 fn set_as_yaml<K: crate::AsYaml, V: crate::AsYaml>(&self, key: K, value: V) {
702 let flow_context = self.is_flow_style();
704
705 let use_explicit_keys = self.uses_explicit_keys();
707
708 for (i, child) in self.0.children_with_tokens().enumerate() {
710 if let Some(node) = child.as_node() {
711 if node.kind() == SyntaxKind::MAPPING_ENTRY {
712 if let Some(entry) = MappingEntry::cast(node.clone()) {
713 if let Some(entry_key_node) = entry.key() {
715 if key_content_matches(&entry_key_node, &key) {
716 entry.set_value(value, flow_context);
718
719 self.0.splice_children(i..i + 1, vec![entry.0.into()]);
720 return;
721 }
722 }
723 }
724 }
725 }
726 }
727
728 let new_entry = MappingEntry::new(key, value, flow_context, use_explicit_keys);
730 self.insert_entry_cst(&new_entry.0);
731 }
732
733 fn insert_entry_cst(&self, new_entry: &SyntaxNode) {
735 let mut count = 0;
737 let mut last_mapping_entry: Option<SyntaxNode> = None;
738
739 for child in self.0.children_with_tokens() {
740 count += 1;
741 if let Some(node) = child.as_node() {
742 if node.kind() == SyntaxKind::MAPPING_ENTRY {
743 last_mapping_entry = Some(node.clone());
744 }
745 }
746 }
747
748 let has_trailing_newline = if let Some(entry) = &last_mapping_entry {
751 entry
752 .last_token()
753 .map(|t| t.kind() == SyntaxKind::NEWLINE)
754 .unwrap_or(false)
755 } else {
756 self.0
758 .last_token()
759 .map(|t| t.kind() == SyntaxKind::NEWLINE)
760 .unwrap_or(false)
761 };
762
763 let mut new_elements = Vec::new();
764
765 let insert_pos = count;
767
768 let indent_level = self.detect_indentation_level();
770 if indent_level > 0 && count > 0 {
771 let mut builder = rowan::GreenNodeBuilder::new();
772 builder.start_node(SyntaxKind::ROOT.into());
773 if !has_trailing_newline {
775 builder.token(SyntaxKind::NEWLINE.into(), "\n");
776 }
777 builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent_level));
778 builder.finish_node();
779 let node = SyntaxNode::new_root_mut(builder.finish());
780 for child in node.children_with_tokens() {
782 if let rowan::NodeOrToken::Token(token) = child {
783 new_elements.push(token.into());
784 }
785 }
786 } else if count > 0 && !has_trailing_newline {
787 let mut builder = rowan::GreenNodeBuilder::new();
789 builder.start_node(SyntaxKind::ROOT.into());
790 builder.token(SyntaxKind::NEWLINE.into(), "\n");
791 builder.finish_node();
792 let node = SyntaxNode::new_root_mut(builder.finish());
793 if let Some(token) = node.first_token() {
794 new_elements.push(token.into());
795 }
796 }
797
798 new_elements.push(new_entry.clone().into());
799
800 self.0.splice_children(insert_pos..insert_pos, new_elements);
804 }
805
806 pub(crate) fn compare_key_nodes(&self, actual: &SyntaxNode, expected: &SyntaxNode) -> bool {
808 if actual.kind() != SyntaxKind::KEY || expected.kind() != SyntaxKind::KEY {
810 return actual.kind() == expected.kind()
811 && self.compare_nodes_structurally(actual, expected);
812 }
813
814 let actual_content = self.get_key_content_nodes(actual);
816 let expected_content = self.get_key_content_nodes(expected);
817
818 if actual_content.len() != expected_content.len() {
819 return false;
820 }
821
822 for (a, e) in actual_content.iter().zip(expected_content.iter()) {
823 if !self.compare_nodes_structurally(a, e) {
824 return false;
825 }
826 }
827
828 true
829 }
830
831 fn get_key_content_nodes(&self, key_node: &SyntaxNode) -> Vec<SyntaxNode> {
833 let mut nodes = Vec::new();
834 for child in key_node.children_with_tokens() {
835 match child {
836 rowan::NodeOrToken::Node(n) => {
837 nodes.push(n);
839 }
840 rowan::NodeOrToken::Token(t) => {
841 if t.kind() != SyntaxKind::WHITESPACE
843 && t.kind() != SyntaxKind::INDENT
844 && t.kind() != SyntaxKind::QUESTION
845 {
846 let mut token_builder = GreenNodeBuilder::new();
848 token_builder.start_node(t.kind().into());
849 token_builder.token(t.kind().into(), t.text());
850 token_builder.finish_node();
851 nodes.push(SyntaxNode::new_root_mut(token_builder.finish()));
852 }
853 }
854 }
855 }
856 nodes
857 }
858
859 fn compare_nodes_structurally(&self, node1: &SyntaxNode, node2: &SyntaxNode) -> bool {
861 if node1.kind() != node2.kind() {
862 return false;
863 }
864
865 match node1.kind() {
866 SyntaxKind::SCALAR => {
867 let s1 = Scalar::cast(node1.clone()).map(|s| s.as_string());
871 let s2 = Scalar::cast(node2.clone()).map(|s| s.as_string());
872 s1 == s2 && s1.is_some()
873 }
874 SyntaxKind::STRING => {
875 let mut iter1 = node1
877 .children_with_tokens()
878 .filter_map(|c| c.into_token())
879 .filter(|t| t.kind() == SyntaxKind::STRING);
880 let mut iter2 = node2
881 .children_with_tokens()
882 .filter_map(|c| c.into_token())
883 .filter(|t| t.kind() == SyntaxKind::STRING);
884 loop {
885 match (iter1.next(), iter2.next()) {
886 (Some(a), Some(b)) if a.text() == b.text() => continue,
887 (None, None) => return true,
888 _ => return false,
889 }
890 }
891 }
892 SyntaxKind::SEQUENCE => {
893 let mut entries1 = node1
895 .children()
896 .filter(|n| n.kind() == SyntaxKind::SEQUENCE_ENTRY);
897 let mut entries2 = node2
898 .children()
899 .filter(|n| n.kind() == SyntaxKind::SEQUENCE_ENTRY);
900 loop {
901 match (entries1.next(), entries2.next()) {
902 (Some(e1), Some(e2)) => {
903 if !self.compare_sequence_entries(&e1, &e2) {
904 return false;
905 }
906 }
907 (None, None) => return true,
908 _ => return false,
909 }
910 }
911 }
912 SyntaxKind::MAPPING => {
913 let mut entries1 = node1
915 .children()
916 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY);
917 let mut entries2 = node2
918 .children()
919 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY);
920 loop {
921 match (entries1.next(), entries2.next()) {
922 (Some(e1), Some(e2)) => {
923 if !self.compare_mapping_entries(&e1, &e2) {
924 return false;
925 }
926 }
927 (None, None) => return true,
928 _ => return false,
929 }
930 }
931 }
932 _ => {
933 let filter_tokens = |node: &SyntaxNode| {
935 node.children_with_tokens()
936 .filter_map(|c| c.into_token())
937 .filter(|t| {
938 t.kind() != SyntaxKind::WHITESPACE && t.kind() != SyntaxKind::INDENT
939 })
940 };
941 let mut iter1 = filter_tokens(node1);
942 let mut iter2 = filter_tokens(node2);
943 loop {
944 match (iter1.next(), iter2.next()) {
945 (Some(a), Some(b)) if a.kind() == b.kind() && a.text() == b.text() => {
946 continue
947 }
948 (None, None) => return true,
949 _ => return false,
950 }
951 }
952 }
953 }
954 }
955
956 fn compare_sequence_entries(&self, entry1: &SyntaxNode, entry2: &SyntaxNode) -> bool {
958 let value1 = entry1.children().find(|n| n.kind() == SyntaxKind::VALUE);
959 let value2 = entry2.children().find(|n| n.kind() == SyntaxKind::VALUE);
960
961 match (value1, value2) {
962 (Some(v1), Some(v2)) => self.compare_nodes_structurally(&v1, &v2),
963 (None, None) => true,
964 _ => false,
965 }
966 }
967
968 fn compare_mapping_entries(&self, entry1: &SyntaxNode, entry2: &SyntaxNode) -> bool {
970 let key1 = entry1.children().find(|n| n.kind() == SyntaxKind::KEY);
971 let key2 = entry2.children().find(|n| n.kind() == SyntaxKind::KEY);
972 let value1 = entry1.children().find(|n| n.kind() == SyntaxKind::VALUE);
973 let value2 = entry2.children().find(|n| n.kind() == SyntaxKind::VALUE);
974
975 match ((key1, value1), (key2, value2)) {
976 ((Some(k1), Some(v1)), (Some(k2), Some(v2))) => {
977 self.compare_key_nodes(&k1, &k2) && self.compare_nodes_structurally(&v1, &v2)
978 }
979 ((Some(k1), None), (Some(k2), None)) => self.compare_key_nodes(&k1, &k2),
980 ((None, Some(v1)), (None, Some(v2))) => self.compare_nodes_structurally(&v1, &v2),
981 ((None, None), (None, None)) => true,
982 _ => false,
983 }
984 }
985
986 pub fn set_with_field_order<I, K>(
991 &self,
992 key: impl crate::AsYaml,
993 value: impl crate::AsYaml,
994 field_order: I,
995 ) where
996 I: IntoIterator<Item = K>,
997 K: crate::AsYaml,
998 {
999 let field_order: Vec<K> = field_order.into_iter().collect();
1001
1002 let children: Vec<_> = self.0.children_with_tokens().collect();
1004 for child in children.iter() {
1005 if let Some(node) = child.as_node() {
1006 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1007 if let Some(key_node) = node.children().find(|n| n.kind() == SyntaxKind::KEY) {
1008 if key_content_matches(&key_node, &key) {
1009 self.set_as_yaml(&key, &value);
1011 return;
1012 }
1013 }
1014 }
1015 }
1016 }
1017
1018 let key_position_in_order = field_order
1021 .iter()
1022 .position(|field| crate::as_yaml::yaml_eq(&key, field));
1023
1024 if let Some(key_index) = key_position_in_order {
1025 let mut insert_after_node: Option<SyntaxNode> = None;
1027 let mut insert_before_node: Option<SyntaxNode> = None;
1028
1029 for field in field_order.iter().take(key_index).rev() {
1031 for child in children.iter() {
1032 if let Some(node) = child.as_node() {
1033 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1034 if let Some(key_node) =
1035 node.children().find(|n| n.kind() == SyntaxKind::KEY)
1036 {
1037 if key_content_matches(&key_node, field) {
1038 insert_after_node = Some(node.clone());
1039 break;
1040 }
1041 }
1042 }
1043 }
1044 }
1045 if insert_after_node.is_some() {
1046 break;
1047 }
1048 }
1049
1050 if insert_after_node.is_none() {
1053 for child in children.iter() {
1054 if let Some(node) = child.as_node() {
1055 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1056 if let Some(existing_key_node) =
1057 node.children().find(|n| n.kind() == SyntaxKind::KEY)
1058 {
1059 let existing_key_position = field_order.iter().position(|field| {
1061 key_content_matches(&existing_key_node, field)
1062 });
1063
1064 if let Some(existing_pos) = existing_key_position {
1066 if existing_pos > key_index {
1067 insert_before_node = Some(node.clone());
1068 break;
1069 }
1070 }
1071 }
1072 }
1073 }
1074 }
1075 }
1076
1077 let flow_context = self.is_flow_style();
1079 let use_explicit_keys = self.uses_explicit_keys();
1080 let new_entry = MappingEntry::new(&key, &value, flow_context, use_explicit_keys).0;
1081
1082 if let Some(after_node) = insert_after_node {
1084 let idx = children
1086 .iter()
1087 .position(|c| c.as_node() == Some(&after_node))
1088 .expect("after_node was found in children earlier");
1089
1090 let has_trailing_newline = after_node
1092 .last_token()
1093 .map(|t| t.kind() == SyntaxKind::NEWLINE)
1094 .unwrap_or(false);
1095
1096 if !has_trailing_newline {
1097 let entry_children_count = after_node.children_with_tokens().count();
1099 let mut nl_builder = GreenNodeBuilder::new();
1100 nl_builder.start_node(SyntaxKind::ROOT.into());
1101 nl_builder.token(SyntaxKind::NEWLINE.into(), "\n");
1102 nl_builder.finish_node();
1103 let nl_node = SyntaxNode::new_root_mut(nl_builder.finish());
1104 if let Some(token) = nl_node.first_token() {
1105 after_node.splice_children(
1106 entry_children_count..entry_children_count,
1107 vec![token.into()],
1108 );
1109 }
1110 }
1111
1112 self.0
1114 .splice_children(idx + 1..idx + 1, vec![new_entry.into()]);
1115 } else if let Some(before_node) = insert_before_node {
1116 let idx = children
1118 .iter()
1119 .position(|c| c.as_node() == Some(&before_node))
1120 .expect("before_node was found in children earlier");
1121
1122 if idx > 0 {
1124 if let Some(prev_entry) = children[..idx].iter().rev().find_map(|c| {
1125 c.as_node()
1126 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
1127 }) {
1128 let has_trailing_newline = prev_entry
1129 .last_token()
1130 .map(|t| t.kind() == SyntaxKind::NEWLINE)
1131 .unwrap_or(false);
1132
1133 if !has_trailing_newline {
1134 let entry_children_count = prev_entry.children_with_tokens().count();
1135 let mut nl_builder = GreenNodeBuilder::new();
1136 nl_builder.start_node(SyntaxKind::ROOT.into());
1137 nl_builder.token(SyntaxKind::NEWLINE.into(), "\n");
1138 nl_builder.finish_node();
1139 let nl_node = SyntaxNode::new_root_mut(nl_builder.finish());
1140 if let Some(token) = nl_node.first_token() {
1141 prev_entry.splice_children(
1142 entry_children_count..entry_children_count,
1143 vec![token.into()],
1144 );
1145 }
1146 }
1147 }
1148 }
1149
1150 self.0.splice_children(idx..idx, vec![new_entry.into()]);
1152 } else {
1153 self.set_as_yaml(&key, &value);
1155 }
1156 } else {
1157 self.set_as_yaml(&key, &value);
1159 }
1160 }
1161
1162 pub fn detect_indentation_level(&self) -> usize {
1166 for child in self.0.children_with_tokens() {
1168 if let Some(token) = child.as_token() {
1169 if token.kind() == SyntaxKind::INDENT {
1170 return token.text().len();
1171 }
1172 }
1173 }
1174 0 }
1176
1177 pub fn move_after(
1189 &self,
1190 after_key: impl crate::AsYaml,
1191 new_key: impl crate::AsYaml,
1192 new_value: impl crate::AsYaml,
1193 ) -> bool {
1194 self.move_after_impl(after_key, new_key, new_value)
1195 }
1196
1197 fn move_after_impl(
1199 &self,
1200 after_key: impl crate::AsYaml,
1201 new_key: impl crate::AsYaml,
1202 new_value: impl crate::AsYaml,
1203 ) -> bool {
1204 let children: Vec<_> = self.0.children_with_tokens().collect();
1205 let mut insert_position = None;
1206 let mut found_key = false;
1207 let mut last_value_end = 0;
1208
1209 let mut i = 0;
1211 let mut removed_existing = false;
1212 while i < children.len() {
1213 if let Some(node) = children[i].as_node() {
1214 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1215 for key_child in node.children() {
1217 if key_child.kind() == SyntaxKind::KEY
1218 && key_content_matches(&key_child, &new_key)
1219 {
1220 let mut remove_range = i..i + 1;
1222
1223 if i + 1 < children.len() {
1225 if let Some(token) = children[i + 1].as_token() {
1226 if token.kind() == SyntaxKind::NEWLINE {
1227 remove_range = i..i + 2;
1228 }
1229 }
1230 }
1231
1232 self.0.splice_children(remove_range, vec![]);
1233 removed_existing = true;
1234 break;
1235 }
1236 }
1237 if removed_existing {
1238 break;
1240 }
1241 }
1242 }
1243 if !removed_existing {
1244 i += 1;
1245 }
1246 }
1247
1248 let children = if removed_existing {
1250 self.0.children_with_tokens().collect()
1251 } else {
1252 children
1253 };
1254
1255 for (i, child) in children.iter().enumerate() {
1257 if let Some(node) = child.as_node() {
1258 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1259 if found_key {
1260 let is_root_level = if i > 0 {
1263 children
1264 .get(i - 1)
1265 .and_then(|c| c.as_token())
1266 .map(|t| t.kind() != SyntaxKind::INDENT)
1267 .unwrap_or(true)
1268 } else {
1269 true
1270 };
1271
1272 if is_root_level {
1273 insert_position = Some(i);
1274 break;
1275 }
1276 }
1277 for key_child in node.children() {
1279 if key_child.kind() == SyntaxKind::KEY
1280 && key_content_matches(&key_child, &after_key)
1281 {
1282 found_key = true;
1283 last_value_end = i + 1; break;
1285 }
1286 }
1287 } else if node.kind() == SyntaxKind::KEY {
1288 if key_content_matches(node, &after_key) {
1289 found_key = true;
1290 }
1291 } else if node.kind() == SyntaxKind::SCALAR {
1292 if key_content_matches(node, &after_key) && !found_key {
1294 found_key = true;
1296 for (j, child_j) in children[(i + 1)..].iter().enumerate() {
1298 if let Some(n) = child_j.as_node() {
1299 if n.kind() == SyntaxKind::VALUE || n.kind() == SyntaxKind::SCALAR {
1300 last_value_end = i + 1 + j + 1;
1301 break;
1302 }
1303 }
1304 }
1305 }
1306 } else if node.kind() == SyntaxKind::VALUE && found_key {
1307 last_value_end = i + 1;
1309 }
1310 } else if let Some(token) = child.as_token() {
1311 if found_key && token.kind() == SyntaxKind::COMMENT {
1312 if i > 0 {
1317 if let Some(prev) = children.get(i - 1) {
1318 let is_top_level = if let Some(prev_token) = prev.as_token() {
1319 prev_token.kind() == SyntaxKind::NEWLINE
1321 } else if let Some(prev_node) = prev.as_node() {
1322 prev_node.kind() == SyntaxKind::MAPPING_ENTRY
1325 } else {
1326 false
1327 };
1328
1329 if is_top_level {
1330 insert_position = Some(i);
1332 break;
1333 }
1334 }
1335 }
1336 } else if found_key && token.kind() == SyntaxKind::NEWLINE {
1337 let is_root_level = if i > 0 {
1340 children
1341 .get(i - 1)
1342 .and_then(|c| c.as_token())
1343 .map(|t| t.kind() != SyntaxKind::INDENT)
1344 .unwrap_or(true)
1345 } else {
1346 true
1347 };
1348
1349 if is_root_level && i + 1 < children.len() {
1350 if let Some(next) = children.get(i + 1) {
1351 if let Some(next_token) = next.as_token() {
1352 if next_token.kind() == SyntaxKind::NEWLINE
1353 || next_token.kind() == SyntaxKind::COMMENT
1354 {
1355 insert_position = Some(i);
1360 break;
1361 }
1362 } else if next.as_node().is_some() {
1363 insert_position = Some(i);
1367 break;
1368 }
1369 }
1370 }
1371 }
1372 }
1373 }
1374
1375 if insert_position.is_none() && found_key && last_value_end > 0 {
1377 insert_position = Some(last_value_end);
1378 }
1379
1380 if let Some(pos) = insert_position {
1381 let mut new_elements = Vec::new();
1383
1384 if pos > 0 {
1386 if let Some(prev_entry) = children[..pos].iter().rev().find_map(|child| {
1388 child
1389 .as_node()
1390 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
1391 }) {
1392 let has_newline = prev_entry
1394 .last_token()
1395 .map(|t| t.kind() == SyntaxKind::NEWLINE)
1396 .unwrap_or(false);
1397
1398 if !has_newline {
1400 let entry_children_count = prev_entry.children_with_tokens().count();
1401 let mut nl_builder = GreenNodeBuilder::new();
1402 nl_builder.start_node(SyntaxKind::ROOT.into());
1403 nl_builder.token(SyntaxKind::NEWLINE.into(), "\n");
1404 nl_builder.finish_node();
1405 let nl_node = SyntaxNode::new_root_mut(nl_builder.finish());
1406 if let Some(token) = nl_node.first_token() {
1407 prev_entry.splice_children(
1408 entry_children_count..entry_children_count,
1409 vec![token.into()],
1410 );
1411 }
1412 }
1413 }
1414 }
1415
1416 let needs_indent = if pos > 0 {
1419 children
1420 .get(pos - 1)
1421 .and_then(|c| c.as_token())
1422 .map(|t| t.kind() == SyntaxKind::INDENT)
1423 .unwrap_or(false)
1424 } else {
1425 false
1426 };
1427
1428 if needs_indent {
1429 let indent_level = self.detect_indentation_level();
1430 if indent_level > 0 {
1431 let mut indent_builder = GreenNodeBuilder::new();
1432 indent_builder.start_node(SyntaxKind::ROOT.into());
1433 indent_builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent_level));
1434 indent_builder.finish_node();
1435 let indent_node = SyntaxNode::new_root_mut(indent_builder.finish());
1436 if let Some(token) = indent_node.first_token() {
1437 new_elements.push(token.into());
1438 }
1439 }
1440 }
1441
1442 let (entry, _has_trailing_newline) = self.create_mapping_entry(&new_key, &new_value);
1444
1445 new_elements.push(entry.into());
1447
1448 self.0.splice_children(pos..pos, new_elements);
1450 true
1451 } else {
1452 false
1453 }
1454 }
1455
1456 pub fn move_before(
1467 &self,
1468 before_key: impl crate::AsYaml,
1469 new_key: impl crate::AsYaml,
1470 new_value: impl crate::AsYaml,
1471 ) -> bool {
1472 self.move_before_impl(before_key, new_key, new_value)
1473 }
1474
1475 fn move_before_impl(
1477 &self,
1478 before_key: impl crate::AsYaml,
1479 new_key: impl crate::AsYaml,
1480 new_value: impl crate::AsYaml,
1481 ) -> bool {
1482 let children: Vec<_> = self.0.children_with_tokens().collect();
1483 let mut insert_position = None;
1484
1485 let mut i = 0;
1487 let mut removed_existing = false;
1488 while i < children.len() {
1489 if let Some(node) = children[i].as_node() {
1490 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1491 for key_child in node.children() {
1493 if key_child.kind() == SyntaxKind::KEY
1494 && key_content_matches(&key_child, &new_key)
1495 {
1496 let mut remove_range = i..i + 1;
1498
1499 if i + 1 < children.len() {
1501 if let Some(token) = children[i + 1].as_token() {
1502 if token.kind() == SyntaxKind::NEWLINE {
1503 remove_range = i..i + 2;
1504 }
1505 }
1506 }
1507
1508 self.0.splice_children(remove_range, vec![]);
1509 removed_existing = true;
1510 break;
1511 }
1512 }
1513 if removed_existing {
1514 break;
1516 }
1517 } else if (node.kind() == SyntaxKind::KEY || node.kind() == SyntaxKind::SCALAR)
1518 && key_content_matches(node, &new_key)
1519 {
1520 for (offset, child_j) in children[(i + 1)..].iter().enumerate() {
1523 if let Some(node) = child_j.as_node() {
1524 if node.kind() == SyntaxKind::VALUE {
1525 let mut value_builder = GreenNodeBuilder::new();
1528 Document::build_value_content(&mut value_builder, &new_value, 2);
1529 let new_value_node =
1530 SyntaxNode::new_root_mut(value_builder.finish());
1531
1532 let j = i + 1 + offset;
1534 self.0
1535 .splice_children(j..j + 1, vec![new_value_node.into()]);
1536 return true;
1537 }
1538 }
1539 }
1540 return false;
1542 }
1543 }
1544 if !removed_existing {
1545 i += 1;
1546 }
1547 }
1548
1549 let children = if removed_existing {
1551 self.0.children_with_tokens().collect()
1552 } else {
1553 children
1554 };
1555
1556 for (i, child) in children.iter().enumerate() {
1558 if let Some(node) = child.as_node() {
1559 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1560 for key_child in node.children() {
1562 if key_child.kind() == SyntaxKind::KEY
1563 && key_content_matches(&key_child, &before_key)
1564 {
1565 let mut line_start = i;
1567 for j in (0..i).rev() {
1568 if let Some(token) = children[j].as_token() {
1569 if token.kind() == SyntaxKind::NEWLINE {
1570 line_start = j + 1;
1571 break;
1572 }
1573 }
1574 }
1575 insert_position = Some(line_start);
1576 break;
1577 }
1578 }
1579 } else if (node.kind() == SyntaxKind::KEY || node.kind() == SyntaxKind::SCALAR)
1580 && key_content_matches(node, &before_key)
1581 {
1582 let mut line_start = i;
1585 for j in (0..i).rev() {
1586 if let Some(token) = children[j].as_token() {
1587 if token.kind() == SyntaxKind::NEWLINE {
1588 line_start = j + 1;
1589 break;
1590 }
1591 }
1592 }
1593 insert_position = Some(line_start);
1594 break;
1595 }
1596 }
1597 }
1598
1599 if let Some(pos) = insert_position {
1600 let mut new_elements = Vec::new();
1605
1606 let (entry, _has_trailing_newline) = self.create_mapping_entry(&new_key, &new_value);
1608 new_elements.push(entry.into());
1609
1610 self.0.splice_children(pos..pos, new_elements);
1615 true
1616 } else {
1617 false
1618 }
1619 }
1620
1621 pub fn insert_at_index_preserving(
1629 &self,
1630 index: usize,
1631 new_key: impl crate::AsYaml,
1632 new_value: impl crate::AsYaml,
1633 ) {
1634 let (new_entry, _has_trailing_newline) = self.create_mapping_entry(new_key, new_value);
1636
1637 if let Some(created_entry) = MappingEntry::cast(new_entry.clone()) {
1639 if let Some(created_key_node) = created_entry.key() {
1640 let existing_entry_opt = self.entries().find(|entry| {
1642 if let Some(entry_key) = entry.key() {
1643 self.compare_key_nodes(&entry_key, &created_key_node)
1644 } else {
1645 false
1646 }
1647 });
1648
1649 if let Some(existing_entry) = existing_entry_opt {
1650 let children: Vec<_> = self.0.children_with_tokens().collect();
1652 for (i, child) in children.iter().enumerate() {
1653 let Some(node) = child.as_node() else {
1654 continue;
1655 };
1656 if node != existing_entry.syntax() {
1657 continue;
1658 };
1659
1660 self.0.splice_children(i..i + 1, vec![new_entry.into()]);
1663
1664 return;
1665 }
1666 }
1667 }
1668 }
1669
1670 let children: Vec<_> = self.0.children_with_tokens().collect();
1672 let mut entry_indices = Vec::new();
1674 for (i, child) in children.iter().enumerate() {
1675 if let Some(node) = child.as_node() {
1676 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1677 entry_indices.push(i);
1678 }
1679 }
1680 }
1681
1682 let mut new_elements = Vec::new();
1683
1684 let insert_pos = if entry_indices.is_empty() {
1686 0
1688 } else if index >= entry_indices.len() {
1689 let last_entry_idx = entry_indices[entry_indices.len() - 1];
1691 let mut pos = last_entry_idx + 1;
1693 while pos < children.len() {
1694 if let Some(token) = children[pos].as_token() {
1695 if token.kind() == SyntaxKind::NEWLINE {
1696 pos += 1;
1697 break;
1698 }
1699 }
1700 pos += 1;
1701 }
1702 pos
1703 } else {
1704 entry_indices[index]
1706 };
1707
1708 if insert_pos > 0 && !entry_indices.is_empty() {
1711 let has_newline_before = if let Some(child) = children.get(insert_pos - 1) {
1712 match child {
1713 rowan::NodeOrToken::Token(t) => t.kind() == SyntaxKind::NEWLINE,
1714 rowan::NodeOrToken::Node(n) => ends_with_newline(n),
1715 }
1716 } else {
1717 false
1718 };
1719
1720 if !has_newline_before {
1721 add_newline_token(&mut new_elements);
1722 }
1723 }
1724
1725 new_elements.push(new_entry.into());
1727
1728 self.0.splice_children(insert_pos..insert_pos, new_elements);
1730 }
1731
1732 pub fn remove(&self, key: impl crate::AsYaml) -> Option<MappingEntry> {
1741 let children: Vec<_> = self.0.children_with_tokens().collect();
1742
1743 for (i, child) in children.iter().enumerate() {
1745 if let Some(node) = child.as_node() {
1746 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1747 if let Some(entry) = MappingEntry::cast(node.clone()) {
1748 if entry.key_matches(&key) {
1749 let is_last = !children.iter().skip(i + 1).any(|c| {
1751 c.as_node()
1752 .is_some_and(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
1753 });
1754
1755 self.0.splice_children(i..(i + 1), vec![]);
1757
1758 if is_last && i > 0 {
1759 if let Some(prev_entry_node) =
1762 children[..i].iter().rev().find_map(|c| {
1763 c.as_node()
1764 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
1765 })
1766 {
1767 if let Some(last_token) = prev_entry_node.last_token() {
1769 if last_token.kind() == SyntaxKind::NEWLINE {
1770 let entry_children_count =
1771 prev_entry_node.children_with_tokens().count();
1772 prev_entry_node.splice_children(
1773 (entry_children_count - 1)..entry_children_count,
1774 vec![],
1775 );
1776 }
1777 }
1778 }
1779 }
1780 return Some(entry);
1781 }
1782 }
1783 }
1784 }
1785 }
1786 None
1787 }
1788
1789 pub fn remove_nth_occurrence(&self, key: impl crate::AsYaml, n: usize) -> Option<MappingEntry> {
1825 let children: Vec<_> = self.0.children_with_tokens().collect();
1826
1827 let mut occurrence_count = 0;
1829 for (i, child) in children.iter().enumerate() {
1830 let node = child.as_node()?;
1831 if node.kind() != SyntaxKind::MAPPING_ENTRY {
1832 continue;
1833 }
1834
1835 let entry = MappingEntry::cast(node.clone())?;
1836 if !entry.key_matches(&key) {
1837 continue;
1838 }
1839
1840 if occurrence_count != n {
1841 occurrence_count += 1;
1842 continue;
1843 }
1844
1845 let is_last = !children.iter().skip(i + 1).any(|c| {
1847 c.as_node()
1848 .is_some_and(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
1849 });
1850
1851 self.0.splice_children(i..(i + 1), vec![]);
1852
1853 if is_last && i > 0 {
1854 let prev_entry_node = children[..i].iter().rev().find_map(|c| {
1856 c.as_node()
1857 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
1858 })?;
1859
1860 if let Some(last_token) = prev_entry_node.last_token() {
1861 if last_token.kind() == SyntaxKind::NEWLINE {
1862 let entry_children_count = prev_entry_node.children_with_tokens().count();
1863 prev_entry_node.splice_children(
1864 (entry_children_count - 1)..entry_children_count,
1865 vec![],
1866 );
1867 }
1868 }
1869 }
1870 return Some(entry);
1871 }
1872 None
1873 }
1874
1875 pub fn clear(&self) {
1879 let keys: Vec<crate::as_yaml::YamlNode> = self.keys().collect();
1880 for key in keys {
1881 self.remove(key);
1882 }
1883 }
1884
1885 pub fn rename_key(&self, old_key: impl crate::AsYaml, new_key: impl crate::AsYaml) -> bool {
1894 let children: Vec<_> = self.0.children_with_tokens().collect();
1895
1896 for (i, child) in children.iter().enumerate() {
1897 if let Some(node) = child.as_node() {
1898 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1899 if let Some(key_node) = node.children().find(|n| n.kind() == SyntaxKind::KEY) {
1900 if key_content_matches(&key_node, &old_key) {
1901 let entry_children: Vec<_> = node.children_with_tokens().collect();
1902
1903 let mut builder = GreenNodeBuilder::new();
1904 builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
1905
1906 for entry_child in entry_children {
1907 match entry_child {
1908 rowan::NodeOrToken::Node(n) if n.kind() == SyntaxKind::KEY => {
1909 builder.start_node(SyntaxKind::KEY.into());
1911 new_key.build_content(&mut builder, 0, false);
1912 builder.finish_node(); }
1914 rowan::NodeOrToken::Node(n) => {
1915 crate::yaml::copy_node_to_builder(&mut builder, &n);
1916 }
1917 rowan::NodeOrToken::Token(t) => {
1918 builder.token(t.kind().into(), t.text());
1919 }
1920 }
1921 }
1922
1923 builder.finish_node();
1924 let new_entry = SyntaxNode::new_root_mut(builder.finish());
1925 self.0.splice_children(i..i + 1, vec![new_entry.into()]);
1926 return true;
1927 }
1928 }
1929 }
1930 }
1931 }
1932 false
1933 }
1934
1935 fn create_mapping_entry(
1937 &self,
1938 key: impl crate::AsYaml,
1939 value: impl crate::AsYaml,
1940 ) -> (SyntaxNode, bool) {
1941 let mut builder = GreenNodeBuilder::new();
1942 builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
1943
1944 builder.start_node(SyntaxKind::KEY.into());
1946 key.build_content(&mut builder, 0, false);
1947 builder.finish_node(); builder.token(SyntaxKind::COLON.into(), ":");
1951
1952 if !value.is_inline() {
1954 builder.token(SyntaxKind::NEWLINE.into(), "\n");
1955 builder.token(SyntaxKind::INDENT.into(), " "); } else {
1957 builder.token(SyntaxKind::WHITESPACE.into(), " ");
1958 }
1959
1960 builder.start_node(SyntaxKind::VALUE.into());
1962 let value_ends_with_newline = value.build_content(&mut builder, 2, false);
1963 builder.finish_node(); let added_newline = if !value_ends_with_newline {
1968 builder.token(SyntaxKind::NEWLINE.into(), "\n");
1969 true
1970 } else {
1971 false
1972 };
1973
1974 builder.finish_node(); (
1976 SyntaxNode::new_root_mut(builder.finish()),
1977 added_newline || value_ends_with_newline,
1978 )
1979 }
1980
1981 pub fn insert_after(
1993 &self,
1994 after_key: impl crate::AsYaml,
1995 key: impl crate::AsYaml,
1996 value: impl crate::AsYaml,
1997 ) -> bool {
1998 if self.find_entry_by_key(&key).is_some() {
2000 self.set_as_yaml(&key, &value);
2001 return true;
2002 }
2003
2004 self.move_after(after_key, key, value)
2009 }
2010
2011 pub fn insert_before(
2023 &self,
2024 before_key: impl crate::AsYaml,
2025 key: impl crate::AsYaml,
2026 value: impl crate::AsYaml,
2027 ) -> bool {
2028 if self.find_entry_by_key(&key).is_some() {
2030 self.set_as_yaml(&key, &value);
2031 return self.find_entry_by_key(&before_key).is_some();
2034 }
2035
2036 self.move_before(before_key, key, value)
2040 }
2041
2042 pub fn insert_at_index(
2051 &self,
2052 index: usize,
2053 key: impl crate::AsYaml,
2054 value: impl crate::AsYaml,
2055 ) {
2056 if self.find_entry_by_key(&key).is_some() {
2058 self.set_as_yaml(&key, &value);
2059 return;
2060 }
2061
2062 let flow_context = self.is_flow_style();
2064 let use_explicit_keys = self.uses_explicit_keys();
2065 let new_entry = MappingEntry::new(&key, &value, flow_context, use_explicit_keys);
2066
2067 let entry_count = self.entries().count();
2069 let actual_index = index.min(entry_count);
2070
2071 let mut entry_positions = Vec::new();
2073 for (i, child) in self.0.children_with_tokens().enumerate() {
2074 if child
2075 .as_node()
2076 .is_some_and(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
2077 {
2078 entry_positions.push(i);
2079 }
2080 }
2081
2082 let insert_pos = if actual_index < entry_positions.len() {
2084 entry_positions[actual_index]
2085 } else {
2086 self.0.children_with_tokens().count()
2087 };
2088
2089 let mut new_elements = Vec::new();
2091
2092 if insert_pos > 0 {
2094 let needs_newline =
2098 if let Some(prev) = self.0.children_with_tokens().nth(insert_pos - 1) {
2099 match prev {
2100 rowan::NodeOrToken::Token(t) => t.kind() != SyntaxKind::NEWLINE,
2101 rowan::NodeOrToken::Node(n) => !ends_with_newline(&n),
2102 }
2103 } else {
2104 false
2105 };
2106
2107 if needs_newline {
2108 add_newline_token(&mut new_elements);
2109 }
2110
2111 let indent_level = self.detect_indentation_level();
2113 if indent_level > 0 {
2114 let mut indent_builder = GreenNodeBuilder::new();
2115 indent_builder.start_node(SyntaxKind::ROOT.into());
2116 indent_builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent_level));
2117 indent_builder.finish_node();
2118 let indent_node = SyntaxNode::new_root_mut(indent_builder.finish());
2119 if let Some(token) = indent_node.first_token() {
2120 new_elements.push(token.into());
2121 }
2122 }
2123 }
2124
2125 new_elements.push(new_entry.0.into());
2127
2128 self.0.splice_children(insert_pos..insert_pos, new_elements);
2130 }
2131
2132 pub fn byte_range(&self) -> crate::TextPosition {
2136 self.0.text_range().into()
2137 }
2138
2139 pub fn start_position(&self, source_text: &str) -> crate::LineColumn {
2148 let range = self.byte_range();
2149 crate::byte_offset_to_line_column(source_text, range.start as usize)
2150 }
2151
2152 pub fn end_position(&self, source_text: &str) -> crate::LineColumn {
2161 let range = self.byte_range();
2162 crate::byte_offset_to_line_column(source_text, range.end as usize)
2163 }
2164}
2165
2166impl Default for Mapping {
2167 fn default() -> Self {
2168 Self::new()
2169 }
2170}
2171
2172impl<'a> IntoIterator for &'a Mapping {
2175 type Item = (crate::as_yaml::YamlNode, crate::as_yaml::YamlNode);
2176 type IntoIter =
2177 Box<dyn Iterator<Item = (crate::as_yaml::YamlNode, crate::as_yaml::YamlNode)> + 'a>;
2178
2179 fn into_iter(self) -> Self::IntoIter {
2180 Box::new(self.iter())
2181 }
2182}
2183
2184impl AsYaml for Mapping {
2185 fn as_node(&self) -> Option<&SyntaxNode> {
2186 Some(&self.0)
2187 }
2188
2189 fn kind(&self) -> YamlKind {
2190 YamlKind::Mapping
2191 }
2192
2193 fn build_content(
2194 &self,
2195 builder: &mut rowan::GreenNodeBuilder,
2196 indent: usize,
2197 _flow_context: bool,
2198 ) -> bool {
2199 builder.start_node(SyntaxKind::MAPPING.into());
2200 crate::as_yaml::copy_node_content_with_indent(builder, &self.0, indent);
2201 builder.finish_node();
2202 self.0
2203 .last_token()
2204 .map(|t| t.kind() == SyntaxKind::NEWLINE)
2205 .unwrap_or(false)
2206 }
2207
2208 fn is_inline(&self) -> bool {
2209 ValueNode::is_inline(self)
2210 }
2211}
2212#[cfg(test)]
2213mod tests {
2214 use crate::scalar::ScalarValue;
2215 use crate::yaml::YamlFile;
2216 use std::str::FromStr;
2217
2218 #[test]
2219 fn test_mapping_set_new_key() {
2220 let yaml = "existing: value";
2221 let parsed = YamlFile::from_str(yaml).unwrap();
2222
2223 let doc = parsed.document().expect("Should have a document");
2225 doc.set("new_key", "new_value");
2226
2227 let output = doc.to_string();
2228
2229 let expected = r#"existing: value
2230new_key: new_value"#;
2231 assert_eq!(output.trim(), expected);
2232 }
2233 #[test]
2234 fn test_mapping_rename_key() {
2235 let yaml = "old_name: value";
2236 let parsed = YamlFile::from_str(yaml).unwrap();
2237
2238 let doc = parsed.document().expect("expected a document");
2239 let mapping = doc.as_mapping().expect("expected a mapping");
2240 let renamed = mapping.rename_key("old_name", "new_name");
2241 assert!(renamed);
2242 assert!(doc.contains_key("new_name"));
2243 assert!(!doc.contains_key("old_name"));
2244 }
2245
2246 #[test]
2247 fn test_mapping_remove_key() {
2248 let yaml = "key1: value1\nkey2: value2";
2249 let parsed = YamlFile::from_str(yaml).unwrap();
2250
2251 let doc = parsed.document().expect("expected a document");
2252 let mapping = doc.as_mapping().expect("expected a mapping");
2253 let removed = mapping.remove("key1");
2254 assert!(removed.is_some());
2255 assert!(!doc.contains_key("key1"));
2256 assert!(doc.contains_key("key2"));
2257 }
2258 #[test]
2259 fn test_mapping_simple_set() {
2260 let yaml = "key1: value1";
2261 let parsed = YamlFile::from_str(yaml).unwrap();
2262
2263 let doc = parsed.document().expect("Should have a document");
2265 doc.set("key2", "value2");
2266
2267 let output = doc.to_string();
2268
2269 let expected = r#"key1: value1
2270key2: value2"#;
2271 assert_eq!(output.trim(), expected);
2272 }
2273 #[test]
2274 fn test_mapping_set_preserves_position() {
2275 let yaml = r#"Name: original_name
2277Contact: original_contact
2278Repository: https://github.com/example/repo.git
2279"#;
2280 let parsed = YamlFile::from_str(yaml).unwrap();
2281 let doc = parsed.document().expect("Should have a document");
2282
2283 doc.set("Contact", "updated_contact");
2285
2286 let output = doc.to_string();
2287 let expected = r#"Name: original_name
2288Contact: updated_contact
2289Repository: https://github.com/example/repo.git
2290"#;
2291 assert_eq!(output, expected);
2292 }
2293 #[test]
2294 fn test_mapping_set_preserves_multiple_fields() {
2295 let yaml = r#"Name: tsne
2297Contact: Justin Donaldson <jdonaldson@gmail.com>
2298Archive: CRAN
2299Repository: https://github.com/jdonaldson/rtsne.git
2300"#;
2301 let parsed = YamlFile::from_str(yaml).unwrap();
2302 let doc = parsed.document().expect("Should have a document");
2303
2304 if let Some(mapping) = doc.as_mapping() {
2305 mapping.set("Contact", "New Contact <new@example.com>");
2307 mapping.set("Archive", "PyPI");
2309 }
2310
2311 let output = doc.to_string();
2312 let expected = r#"Name: tsne
2313Contact: New Contact <new@example.com>
2314Archive: PyPI
2315Repository: https://github.com/jdonaldson/rtsne.git
2316"#;
2317 assert_eq!(output, expected);
2318 }
2319 #[test]
2320 fn test_mapping_insert_after() {
2321 let yaml = r#"first: 1
2322second: 2
2323fourth: 4"#;
2324
2325 let parsed = YamlFile::from_str(yaml).unwrap();
2326
2327 let doc = parsed.document().expect("Should have a document");
2328
2329 let success = doc.insert_after("second", "third", 3);
2331 assert!(
2332 success,
2333 "insert_after should succeed when reference key exists"
2334 );
2335
2336 let output = doc.to_string();
2337
2338 let expected = r#"first: 1
2340second: 2
2341third: 3
2342fourth: 4"#;
2343 assert_eq!(output.trim(), expected);
2344
2345 let failed = doc.insert_after("nonexistent", "new_key", "new_value");
2347 assert!(
2348 !failed,
2349 "insert_after should fail when reference key doesn't exist"
2350 );
2351
2352 let updated = doc.insert_after("first", "second", "2_updated");
2354 assert!(updated, "insert_after should update existing key");
2355 let updated_output = doc.to_string();
2356 let expected_updated = r#"first: 1
2357second: 2_updated
2358third: 3
2359fourth: 4"#;
2360 assert_eq!(updated_output.trim(), expected_updated);
2361 }
2362 #[test]
2363 fn test_mapping_insert_before() {
2364 let yaml = r#"first: 1
2365third: 3
2366fourth: 4"#;
2367
2368 let parsed = YamlFile::from_str(yaml).unwrap();
2369 let doc = parsed.document().expect("Should have a document");
2370
2371 let success = doc.insert_before("third", "second", 2);
2373 assert!(
2374 success,
2375 "insert_before should succeed when reference key exists"
2376 );
2377
2378 let output = doc.to_string();
2379
2380 let expected = r#"first: 1
2382second: 2
2383third: 3
2384fourth: 4"#;
2385 assert_eq!(output.trim(), expected);
2386
2387 let failed = doc.insert_before("nonexistent", "new_key", "new_value");
2389 assert!(
2390 !failed,
2391 "insert_before should fail when reference key doesn't exist"
2392 );
2393
2394 let updated = doc.insert_before("fourth", "third", "3_updated");
2396 assert!(updated, "insert_before should update existing key");
2397 let output = doc.to_string();
2398 let expected_updated = r#"first: 1
2399second: 2
2400third: 3_updated
2401fourth: 4"#;
2402 assert_eq!(output.trim(), expected_updated);
2403 }
2404 #[test]
2405 fn test_mapping_insert_at_index() {
2406 let yaml = r#"first: 1
2407third: 3"#;
2408
2409 let parsed = YamlFile::from_str(yaml).unwrap();
2410 let doc = parsed.document().expect("Should have a document");
2411
2412 doc.insert_at_index(1, "second", 2);
2414
2415 let output = doc.to_string();
2416
2417 let expected = r#"first: 1
2419second: 2
2420third: 3"#;
2421 assert_eq!(output.trim(), expected);
2422
2423 doc.insert_at_index(0, "zero", 0);
2425 let output2 = doc.to_string();
2426 let expected2 = r#"zero: 0
2427first: 1
2428second: 2
2429third: 3"#;
2430 assert_eq!(output2.trim(), expected2);
2431
2432 doc.insert_at_index(100, "last", "999");
2434 let output3 = doc.to_string();
2435 let expected3 = r#"zero: 0
2436first: 1
2437second: 2
2438third: 3
2439last: '999'"#;
2440 assert_eq!(output3.trim(), expected3);
2441
2442 doc.insert_at_index(2, "first", "1_updated");
2444 let final_output = doc.to_string();
2445 let expected_final = r#"zero: 0
2446first: 1_updated
2447second: 2
2448third: 3
2449last: '999'"#;
2450 assert_eq!(final_output.trim(), expected_final);
2451 }
2452 #[test]
2453 fn test_mapping_insert_special_characters() {
2454 let yaml = "key1: value1";
2455
2456 let parsed = YamlFile::from_str(yaml).unwrap();
2457 let doc = parsed.document().expect("Should have a document");
2458
2459 doc.insert_after("key1", "special:key", "value:with:colons");
2461 doc.insert_before("key1", "key with spaces", "value with spaces");
2462 doc.insert_at_index(1, "key@symbol", "value#hash");
2463
2464 assert!(doc.contains_key("special:key"));
2466 assert!(doc.contains_key("key with spaces"));
2467 assert!(doc.contains_key("key@symbol"));
2468
2469 let output = doc.to_string();
2471 let reparsed = YamlFile::from_str(&output);
2472 assert!(reparsed.is_ok(), "Output should be valid YAML");
2473 }
2474 #[test]
2475 fn test_mapping_insert_empty_values() {
2476 let yaml = "key1: value1";
2477
2478 let parsed = YamlFile::from_str(yaml).unwrap();
2479 let doc = parsed.document().expect("Should have a document");
2480
2481 doc.insert_after("key1", "empty", "");
2483 doc.insert_before("key1", "null_key", ScalarValue::null());
2484
2485 assert!(doc.contains_key("empty"));
2486 assert!(doc.contains_key("null_key"));
2487
2488 let output = parsed.to_string();
2490 let reparsed = YamlFile::from_str(&output);
2491 assert!(
2492 reparsed.is_ok(),
2493 "Output with empty values should be valid YAML"
2494 );
2495 }
2496
2497 #[test]
2500 fn test_mapping_into_iterator() {
2501 use crate::Document;
2502 let text = "name: Alice\nage: 30\ncity: Boston";
2503 let doc = Document::from_str(text).unwrap();
2504 let mapping = doc.as_mapping().unwrap();
2505
2506 let mut count = 0;
2508 for (key, value) in &mapping {
2509 count += 1;
2510
2511 assert!(key.is_scalar());
2513 assert!(value.is_scalar());
2514 }
2515
2516 assert_eq!(count, 3);
2517 }
2518
2519 #[test]
2520 fn test_mapping_into_iterator_collect() {
2521 use crate::Document;
2522 let text = "a: 1\nb: 2\nc: 3";
2523 let doc = Document::from_str(text).unwrap();
2524 let mapping = doc.as_mapping().unwrap();
2525
2526 let pairs: Vec<_> = (&mapping).into_iter().collect();
2528 assert_eq!(pairs.len(), 3);
2529
2530 for (key, value) in pairs {
2532 assert!(key.as_scalar().is_some());
2533 assert!(value.as_scalar().is_some());
2534 }
2535 }
2536
2537 #[test]
2538 fn test_mapping_iterator_filter() {
2539 use crate::Document;
2540 let text = "a: 1\nb: 2\nc: 3\nd: 4";
2541 let doc = Document::from_str(text).unwrap();
2542 let mapping = doc.as_mapping().unwrap();
2543
2544 let even_count = (&mapping)
2546 .into_iter()
2547 .filter(|(_, value)| {
2548 value
2549 .as_scalar()
2550 .and_then(|s| s.to_string().parse::<i32>().ok())
2551 .map(|n| n % 2 == 0)
2552 .unwrap_or(false)
2553 })
2554 .count();
2555
2556 assert_eq!(even_count, 2); }
2558
2559 #[test]
2560 fn test_empty_mapping_iterator() {
2561 let empty = crate::Mapping::new();
2562
2563 let count = (&empty).into_iter().count();
2564 assert_eq!(count, 0);
2565 }
2566
2567 #[test]
2568 fn test_nested_mapping_iteration() {
2569 use crate::Document;
2570 let text = "server:\n host: localhost\n port: 8080";
2571 let doc = Document::from_str(text).unwrap();
2572 let mapping = doc.as_mapping().unwrap();
2573
2574 for (key, _value) in &mapping {
2576 if let Some(key_scalar) = key.as_scalar() {
2577 if key_scalar.to_string() == "server" {
2578 if let Some(nested_mapping) = mapping.get_mapping("server") {
2580 let nested_count = (&nested_mapping).into_iter().count();
2581 assert_eq!(nested_count, 2); }
2583 }
2584 }
2585 }
2586 }
2587
2588 #[test]
2593 fn test_mapping_keys() {
2594 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
2595 let doc = yaml.document().unwrap();
2596 let mapping = doc.as_mapping().unwrap();
2597
2598 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2599 assert_eq!(keys, vec!["name", "age", "city"]);
2600 }
2601
2602 #[test]
2603 fn test_mapping_is_empty() {
2604 let yaml = YamlFile::from_str("{}").unwrap();
2605 let doc = yaml.document().unwrap();
2606 let mapping = doc.as_mapping().unwrap();
2607 assert!(mapping.is_empty());
2608
2609 let yaml2 = YamlFile::from_str("key: value").unwrap();
2610 let doc2 = yaml2.document().unwrap();
2611 let mapping2 = doc2.as_mapping().unwrap();
2612 assert!(!mapping2.is_empty());
2613 }
2614
2615 #[test]
2616 fn test_mapping_contains_key() {
2617 let yaml = YamlFile::from_str("name: Alice\nage: 30").unwrap();
2618 let doc = yaml.document().unwrap();
2619 let mapping = doc.as_mapping().unwrap();
2620
2621 assert!(mapping.contains_key("name"));
2622 assert!(mapping.contains_key("age"));
2623 assert!(!mapping.contains_key("city"));
2624 }
2625
2626 #[test]
2627 fn test_mapping_get() {
2628 let yaml = YamlFile::from_str("name: Alice\nage: 30").unwrap();
2629 let doc = yaml.document().unwrap();
2630 let mapping = doc.as_mapping().unwrap();
2631
2632 assert_eq!(
2633 mapping
2634 .get("name")
2635 .and_then(|v| v.as_scalar().map(|s| s.as_string())),
2636 Some("Alice".to_string())
2637 );
2638 assert_eq!(mapping.get("age").and_then(|v| v.to_i64()), Some(30));
2639 assert!(mapping.get("city").is_none());
2640 }
2641
2642 #[test]
2643 fn test_mapping_single_entry() {
2644 let yaml = YamlFile::from_str("key: value").unwrap();
2645 let doc = yaml.document().unwrap();
2646 let mapping = doc.as_mapping().unwrap();
2647
2648 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2649 assert_eq!(keys, vec!["key"]);
2650 assert!(!mapping.is_empty());
2651 assert!(mapping.contains_key("key"));
2652 }
2653
2654 #[test]
2657 fn test_mapping_ops_set_new_key() {
2658 let yaml = YamlFile::from_str("name: Alice").unwrap();
2659 let doc = yaml.document().unwrap();
2660 let mapping = doc.as_mapping().unwrap();
2661
2662 mapping.set("age", 30);
2663
2664 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2665 assert_eq!(keys, vec!["name", "age"]);
2666 assert_eq!(mapping.get("age").and_then(|v| v.to_i64()), Some(30));
2667 }
2668
2669 #[test]
2670 fn test_mapping_set_existing_key() {
2671 let yaml = YamlFile::from_str("name: Alice\nage: 30").unwrap();
2672 let doc = yaml.document().unwrap();
2673 let mapping = doc.as_mapping().unwrap();
2674
2675 mapping.set("age", 31);
2676
2677 assert_eq!(mapping.get("age").and_then(|v| v.to_i64()), Some(31));
2678 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2679 assert_eq!(keys, vec!["name", "age"]);
2680 }
2681
2682 #[test]
2683 fn test_mapping_remove_existing_key() {
2684 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
2685 let doc = yaml.document().unwrap();
2686 let mapping = doc.as_mapping().unwrap();
2687
2688 let removed = mapping.remove("age");
2689 assert!(removed.is_some());
2690
2691 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2692 assert_eq!(keys, vec!["name", "city"]);
2693 assert!(!mapping.contains_key("age"));
2694 }
2695
2696 #[test]
2697 fn test_mapping_remove_nonexistent_key() {
2698 let yaml = YamlFile::from_str("name: Alice").unwrap();
2699 let doc = yaml.document().unwrap();
2700 let mapping = doc.as_mapping().unwrap();
2701
2702 let removed = mapping.remove("age");
2703 assert!(removed.is_none());
2704
2705 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2706 assert_eq!(keys, vec!["name"]);
2707 }
2708
2709 #[test]
2710 fn test_mapping_remove_all_keys() {
2711 let yaml = YamlFile::from_str("a: 1\nb: 2").unwrap();
2712 let doc = yaml.document().unwrap();
2713 let mapping = doc.as_mapping().unwrap();
2714
2715 assert!(mapping.remove("a").is_some());
2716 assert!(mapping.remove("b").is_some());
2717 assert!(mapping.is_empty());
2718 }
2719
2720 #[test]
2721 fn test_rename_key_basic() {
2722 let original = r#"name: my-app
2723version: 1.0
2724author: Alice"#;
2725
2726 let yaml = YamlFile::from_str(original).unwrap();
2727
2728 if let Some(doc) = yaml.document() {
2729 if let Some(mapping) = doc.as_mapping() {
2730 let success = mapping.rename_key("version", "app_version");
2731 assert!(success);
2732 }
2733 }
2734
2735 let expected = r#"name: my-app
2736app_version: 1.0
2737author: Alice"#;
2738 assert_eq!(yaml.to_string(), expected);
2739 }
2740
2741 #[test]
2742 fn test_rename_key_preserves_value() {
2743 let original = r#"count: 42
2744enabled: true"#;
2745
2746 let yaml = YamlFile::from_str(original).unwrap();
2747
2748 if let Some(doc) = yaml.document() {
2749 if let Some(mapping) = doc.as_mapping() {
2750 mapping.rename_key("count", "total");
2751 }
2752 }
2753
2754 let expected = r#"total: 42
2755enabled: true"#;
2756 assert_eq!(yaml.to_string(), expected);
2757 }
2758
2759 #[test]
2760 fn test_remove_field() {
2761 let original = r#"name: my-app
2762version: 1.0
2763author: Alice"#;
2764
2765 let yaml = YamlFile::from_str(original).unwrap();
2766
2767 if let Some(doc) = yaml.document() {
2768 if let Some(mapping) = doc.as_mapping() {
2769 let removed = mapping.remove("author");
2770 assert!(removed.is_some());
2771 }
2772 }
2773
2774 let expected = r#"name: my-app
2775version: 1.0"#;
2776 assert_eq!(yaml.to_string(), expected);
2777 }
2778
2779 #[test]
2780 fn test_complex_operations_combined() {
2781 let original = r#"name: my-app
2782version: 1.0
2783author: Alice
2784year: 2023
2785
2786features:
2787 - logging
2788 - auth"#;
2789
2790 let yaml = YamlFile::from_str(original).unwrap();
2791
2792 if let Some(doc) = yaml.document() {
2793 if let Some(mapping) = doc.as_mapping() {
2794 mapping.set("license", "MIT");
2796 mapping.set("published", true);
2797 mapping.set("downloads", 1000);
2798
2799 mapping.remove("author");
2801
2802 mapping.rename_key("version", "app_version");
2804
2805 mapping.set("year", 2024);
2807 }
2808 }
2809
2810 let expected = r#"name: my-app
2811app_version: 1.0
2812year: 2024
2813
2814features:
2815 - logging
2816 - auth
2817license: MIT
2818published: true
2819downloads: 1000
2820"#;
2821 assert_eq!(yaml.to_string(), expected);
2822 }
2823
2824 #[test]
2827 fn test_mapping_get_nested_mapping() {
2828 let yaml = YamlFile::from_str("user:\n name: Alice\n age: 30").unwrap();
2829 let doc = yaml.document().unwrap();
2830 let mapping = doc.as_mapping().unwrap();
2831
2832 let nested = mapping.get_mapping("user");
2833 assert!(nested.is_some());
2834
2835 let nested = nested.unwrap();
2836 assert_eq!(
2837 nested
2838 .get("name")
2839 .and_then(|v| v.as_scalar().map(|s| s.as_string())),
2840 Some("Alice".to_string())
2841 );
2842 assert_eq!(nested.get("age").and_then(|v| v.to_i64()), Some(30));
2843 }
2844
2845 #[test]
2846 fn test_mapping_get_nested_sequence() {
2847 let yaml = YamlFile::from_str("items:\n - a\n - b\n - c").unwrap();
2848 let doc = yaml.document().unwrap();
2849 let mapping = doc.as_mapping().unwrap();
2850
2851 let seq = mapping.get_sequence("items");
2852 assert!(seq.is_some());
2853
2854 let seq = seq.unwrap();
2855 assert_eq!(seq.len(), 3);
2856 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
2857 assert_eq!(values, vec!["a", "b", "c"]);
2858 }
2859
2860 #[test]
2861 fn test_mapping_get_nonexistent_nested() {
2862 let yaml = YamlFile::from_str("name: Alice").unwrap();
2863 let doc = yaml.document().unwrap();
2864 let mapping = doc.as_mapping().unwrap();
2865
2866 assert_eq!(mapping.get_mapping("user"), None);
2867 assert_eq!(mapping.get_sequence("items"), None);
2868 }
2869
2870 #[test]
2873 fn test_rename_key_nonexistent() {
2874 let yaml = YamlFile::from_str("name: Alice").unwrap();
2875 let doc = yaml.document().unwrap();
2876 let mapping = doc.as_mapping().unwrap();
2877
2878 let success = mapping.rename_key("age", "years");
2879 assert!(!success);
2880
2881 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2882 assert_eq!(keys, vec!["name"]);
2883 }
2884
2885 #[test]
2886 fn test_rename_key_first_entry() {
2887 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
2888 let doc = yaml.document().unwrap();
2889 let mapping = doc.as_mapping().unwrap();
2890
2891 let success = mapping.rename_key("name", "username");
2892 assert!(success);
2893
2894 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2895 assert_eq!(keys, vec!["username", "age", "city"]);
2896 }
2897
2898 #[test]
2899 fn test_rename_key_middle_entry() {
2900 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
2901 let doc = yaml.document().unwrap();
2902 let mapping = doc.as_mapping().unwrap();
2903
2904 let success = mapping.rename_key("age", "years");
2905 assert!(success);
2906
2907 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2908 assert_eq!(keys, vec!["name", "years", "city"]);
2909 }
2910
2911 #[test]
2912 fn test_rename_key_last_entry() {
2913 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
2914 let doc = yaml.document().unwrap();
2915 let mapping = doc.as_mapping().unwrap();
2916
2917 let success = mapping.rename_key("city", "location");
2918 assert!(success);
2919
2920 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2921 assert_eq!(keys, vec!["name", "age", "location"]);
2922 }
2923
2924 #[test]
2927 fn test_mapping_with_different_value_types() {
2928 let yaml = YamlFile::from_str("string: hello\nnumber: 42\nbool: true").unwrap();
2929 let doc = yaml.document().unwrap();
2930 let mapping = doc.as_mapping().unwrap();
2931
2932 assert_eq!(
2933 mapping
2934 .get("string")
2935 .and_then(|v| v.as_scalar().map(|s| s.as_string())),
2936 Some("hello".to_string())
2937 );
2938 assert_eq!(mapping.get("number").and_then(|v| v.to_i64()), Some(42));
2939 assert_eq!(mapping.get("bool").and_then(|v| v.to_bool()), Some(true));
2940 }
2941
2942 #[test]
2943 fn test_mapping_set_different_value_types() {
2944 let yaml = YamlFile::from_str("key: value").unwrap();
2945 let doc = yaml.document().unwrap();
2946 let mapping = doc.as_mapping().unwrap();
2947
2948 mapping.set("number", 123);
2949 mapping.set("bool", false);
2950 mapping.set("text", "hello");
2951
2952 assert_eq!(mapping.get("number").and_then(|v| v.to_i64()), Some(123));
2953 assert_eq!(mapping.get("bool").and_then(|v| v.to_bool()), Some(false));
2954 assert_eq!(
2955 mapping
2956 .get("text")
2957 .and_then(|v| v.as_scalar().map(|s| s.as_string())),
2958 Some("hello".to_string())
2959 );
2960 }
2961
2962 #[test]
2965 fn test_empty_mapping_operations() {
2966 let yaml = YamlFile::from_str("{}").unwrap();
2967 let doc = yaml.document().unwrap();
2968 let mapping = doc.as_mapping().unwrap();
2969
2970 assert!(mapping.is_empty());
2971 assert!(!mapping.contains_key("any"));
2972 assert_eq!(mapping.get("any"), None);
2973 assert!(mapping.remove("any").is_none());
2974 assert!(!mapping.rename_key("old", "new"));
2975
2976 mapping.set("first", "value");
2978 assert!(!mapping.is_empty());
2979 assert_eq!(
2981 mapping.get("first").map(|v| v.to_string()),
2982 Some("\"value\"".to_string())
2983 );
2984 }
2985
2986 #[test]
2987 fn test_mapping_remove_first_of_three() {
2988 let yaml = YamlFile::from_str("a: 1\nb: 2\nc: 3").unwrap();
2989 let doc = yaml.document().unwrap();
2990 let mapping = doc.as_mapping().unwrap();
2991
2992 assert!(mapping.remove("a").is_some());
2993
2994 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
2995 assert_eq!(keys, vec!["b", "c"]);
2996 }
2997
2998 #[test]
2999 fn test_mapping_remove_middle_of_three() {
3000 let yaml = YamlFile::from_str("a: 1\nb: 2\nc: 3").unwrap();
3001 let doc = yaml.document().unwrap();
3002 let mapping = doc.as_mapping().unwrap();
3003
3004 assert!(mapping.remove("b").is_some());
3005
3006 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3007 assert_eq!(keys, vec!["a", "c"]);
3008 }
3009
3010 #[test]
3011 fn test_mapping_remove_last_of_three() {
3012 let yaml = YamlFile::from_str("a: 1\nb: 2\nc: 3").unwrap();
3013 let doc = yaml.document().unwrap();
3014 let mapping = doc.as_mapping().unwrap();
3015
3016 assert!(mapping.remove("c").is_some());
3017
3018 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3019 assert_eq!(keys, vec!["a", "b"]);
3020 }
3021
3022 #[test]
3025 fn test_mapping_len_empty() {
3026 let yaml = YamlFile::from_str("{}").unwrap();
3027 let doc = yaml.document().unwrap();
3028 let mapping = doc.as_mapping().unwrap();
3029
3030 assert_eq!(mapping.len(), 0);
3031 assert!(mapping.is_empty());
3032 }
3033
3034 #[test]
3035 fn test_mapping_len_single() {
3036 let yaml = YamlFile::from_str("name: Alice").unwrap();
3037 let doc = yaml.document().unwrap();
3038 let mapping = doc.as_mapping().unwrap();
3039
3040 assert_eq!(mapping.len(), 1);
3041 assert!(!mapping.is_empty());
3042 }
3043
3044 #[test]
3045 fn test_mapping_len_multiple() {
3046 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3047 let doc = yaml.document().unwrap();
3048 let mapping = doc.as_mapping().unwrap();
3049
3050 assert_eq!(mapping.len(), 3);
3051 assert!(!mapping.is_empty());
3052 }
3053
3054 #[test]
3055 fn test_mapping_len_after_adding() {
3056 let yaml = YamlFile::from_str("name: Alice").unwrap();
3057 let doc = yaml.document().unwrap();
3058 let mapping = doc.as_mapping().unwrap();
3059
3060 assert_eq!(mapping.len(), 1);
3061
3062 mapping.set("age", 30);
3063 assert_eq!(mapping.len(), 2);
3064
3065 mapping.set("city", "NYC");
3066 assert_eq!(mapping.len(), 3);
3067 }
3068
3069 #[test]
3070 fn test_mapping_len_after_removing() {
3071 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3072 let doc = yaml.document().unwrap();
3073 let mapping = doc.as_mapping().unwrap();
3074
3075 assert_eq!(mapping.len(), 3);
3076
3077 mapping.remove("age");
3078 assert_eq!(mapping.len(), 2);
3079
3080 mapping.remove("city");
3081 assert_eq!(mapping.len(), 1);
3082
3083 mapping.remove("name");
3084 assert_eq!(mapping.len(), 0);
3085 assert!(mapping.is_empty());
3086 }
3087
3088 #[test]
3089 fn test_mapping_values_empty() {
3090 let yaml = YamlFile::from_str("{}").unwrap();
3091 let doc = yaml.document().unwrap();
3092 let mapping = doc.as_mapping().unwrap();
3093
3094 let values: Vec<_> = mapping.values().collect();
3095 assert_eq!(values.len(), 0);
3096 }
3097
3098 #[test]
3099 fn test_mapping_values_single() {
3100 let yaml = YamlFile::from_str("name: Alice").unwrap();
3101 let doc = yaml.document().unwrap();
3102 let mapping = doc.as_mapping().unwrap();
3103
3104 let values: Vec<_> = mapping.values().collect();
3105 assert_eq!(values.len(), 1);
3106 assert_eq!(
3107 values[0].as_scalar().map(|s| s.as_string()),
3108 Some("Alice".to_string())
3109 );
3110 }
3111
3112 #[test]
3113 fn test_mapping_values_multiple() {
3114 let yaml = YamlFile::from_str("name: Alice\nage: 30\nactive: true").unwrap();
3115 let doc = yaml.document().unwrap();
3116 let mapping = doc.as_mapping().unwrap();
3117
3118 let values: Vec<_> = mapping.values().collect();
3119 assert_eq!(values.len(), 3);
3120 assert_eq!(
3121 values[0].as_scalar().map(|s| s.as_string()),
3122 Some("Alice".to_string())
3123 );
3124 assert_eq!(values[1].to_i64(), Some(30));
3125 assert_eq!(values[2].to_bool(), Some(true));
3126 }
3127
3128 #[test]
3129 fn test_mapping_values_different_types() {
3130 let yaml = YamlFile::from_str("string: hello\nnumber: 42\nbool: false").unwrap();
3131 let doc = yaml.document().unwrap();
3132 let mapping = doc.as_mapping().unwrap();
3133
3134 let values: Vec<_> = mapping.values().collect();
3136 assert_eq!(values.len(), 3);
3137
3138 assert_eq!(
3139 values[0].as_scalar().map(|s| s.as_string()),
3140 Some("hello".to_string())
3141 );
3142 assert_eq!(values[1].to_i64(), Some(42));
3143 assert_eq!(values[2].to_bool(), Some(false));
3144 }
3145
3146 #[test]
3147 fn test_mapping_iter_empty() {
3148 let yaml = YamlFile::from_str("{}").unwrap();
3149 let doc = yaml.document().unwrap();
3150 let mapping = doc.as_mapping().unwrap();
3151
3152 let pairs: Vec<_> = mapping.iter().collect();
3153 assert_eq!(pairs.len(), 0);
3154 }
3155
3156 #[test]
3157 fn test_mapping_iter_single() {
3158 let yaml = YamlFile::from_str("name: Alice").unwrap();
3159 let doc = yaml.document().unwrap();
3160 let mapping = doc.as_mapping().unwrap();
3161
3162 let pairs: Vec<_> = mapping.iter().collect();
3163 assert_eq!(pairs.len(), 1);
3164 assert_eq!(
3165 pairs[0].0.as_scalar().map(|s| s.as_string()),
3166 Some("name".to_string())
3167 );
3168 assert_eq!(
3169 pairs[0].1.as_scalar().map(|s| s.as_string()),
3170 Some("Alice".to_string())
3171 );
3172 }
3173
3174 #[test]
3175 fn test_mapping_iter_multiple() {
3176 let yaml = YamlFile::from_str("name: Alice\nage: 30\nactive: true").unwrap();
3177 let doc = yaml.document().unwrap();
3178 let mapping = doc.as_mapping().unwrap();
3179
3180 let pairs: Vec<_> = mapping.iter().collect();
3181
3182 assert_eq!(pairs.len(), 3);
3183 assert_eq!(
3184 pairs[0].0.as_scalar().map(|s| s.as_string()),
3185 Some("name".to_string())
3186 );
3187 assert_eq!(
3188 pairs[0].1.as_scalar().map(|s| s.as_string()),
3189 Some("Alice".to_string())
3190 );
3191 assert_eq!(
3192 pairs[1].0.as_scalar().map(|s| s.as_string()),
3193 Some("age".to_string())
3194 );
3195 assert_eq!(pairs[1].1.to_i64(), Some(30));
3196 assert_eq!(
3197 pairs[2].0.as_scalar().map(|s| s.as_string()),
3198 Some("active".to_string())
3199 );
3200 assert_eq!(pairs[2].1.to_bool(), Some(true));
3201 }
3202
3203 #[test]
3204 fn test_mapping_iter_different_types() {
3205 let yaml = YamlFile::from_str("string: hello\nnumber: 42\nbool: false").unwrap();
3206 let doc = yaml.document().unwrap();
3207 let mapping = doc.as_mapping().unwrap();
3208
3209 let pairs: Vec<_> = mapping.iter().collect();
3210 assert_eq!(pairs.len(), 3);
3211
3212 assert_eq!(
3214 pairs[0].0.as_scalar().map(|s| s.as_string()),
3215 Some("string".to_string())
3216 );
3217 assert_eq!(
3218 pairs[0].1.as_scalar().map(|s| s.as_string()),
3219 Some("hello".to_string())
3220 );
3221
3222 assert_eq!(
3224 pairs[1].0.as_scalar().map(|s| s.as_string()),
3225 Some("number".to_string())
3226 );
3227 assert_eq!(pairs[1].1.to_i64(), Some(42));
3228
3229 assert_eq!(
3231 pairs[2].0.as_scalar().map(|s| s.as_string()),
3232 Some("bool".to_string())
3233 );
3234 assert_eq!(pairs[2].1.to_bool(), Some(false));
3235 }
3236
3237 #[test]
3238 fn test_mapping_iter_preserves_order() {
3239 let yaml = YamlFile::from_str("z: 1\na: 2\nm: 3").unwrap();
3240 let doc = yaml.document().unwrap();
3241 let mapping = doc.as_mapping().unwrap();
3242
3243 let pairs: Vec<_> = mapping.iter().collect();
3244 assert_eq!(pairs.len(), 3);
3245 assert_eq!(
3246 pairs[0].0.as_scalar().map(|s| s.as_string()),
3247 Some("z".to_string())
3248 );
3249 assert_eq!(
3250 pairs[1].0.as_scalar().map(|s| s.as_string()),
3251 Some("a".to_string())
3252 );
3253 assert_eq!(
3254 pairs[2].0.as_scalar().map(|s| s.as_string()),
3255 Some("m".to_string())
3256 );
3257 }
3258
3259 #[test]
3260 fn test_mapping_clear_empty() {
3261 let yaml = YamlFile::from_str("{}").unwrap();
3262 let doc = yaml.document().unwrap();
3263 let mapping = doc.as_mapping().unwrap();
3264
3265 assert_eq!(mapping.len(), 0);
3266 mapping.clear();
3267 assert_eq!(mapping.len(), 0);
3268 }
3269
3270 #[test]
3271 fn test_mapping_clear_single() {
3272 let yaml = YamlFile::from_str("name: Alice").unwrap();
3273 let doc = yaml.document().unwrap();
3274 let mapping = doc.as_mapping().unwrap();
3275
3276 assert_eq!(mapping.len(), 1);
3277 mapping.clear();
3278 assert_eq!(mapping.len(), 0);
3279 assert!(mapping.is_empty());
3280
3281 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3282 assert_eq!(keys, Vec::<String>::new());
3283 }
3284
3285 #[test]
3286 fn test_mapping_clear_multiple() {
3287 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3288 let doc = yaml.document().unwrap();
3289 let mapping = doc.as_mapping().unwrap();
3290
3291 assert_eq!(mapping.len(), 3);
3292 mapping.clear();
3293 assert_eq!(mapping.len(), 0);
3294 assert!(mapping.is_empty());
3295
3296 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3297 assert_eq!(keys, Vec::<String>::new());
3298 }
3299
3300 #[test]
3301 fn test_mapping_clear_and_add() {
3302 let yaml = YamlFile::from_str("name: Alice\nage: 30").unwrap();
3303 let doc = yaml.document().unwrap();
3304 let mapping = doc.as_mapping().unwrap();
3305
3306 assert_eq!(mapping.len(), 2);
3307 mapping.clear();
3308 assert_eq!(mapping.len(), 0);
3309
3310 mapping.set("new_key", "new_value");
3312 assert_eq!(mapping.len(), 1);
3313 let value = mapping.get("new_key").unwrap();
3314 assert_eq!(
3315 value.as_scalar().map(|s| s.as_string()),
3316 Some("new_value".to_string())
3317 );
3318 }
3319
3320 #[test]
3321 fn test_mapping_clear_large() {
3322 let yaml_str = (0..100)
3324 .map(|i| format!("key{}: value{}", i, i))
3325 .collect::<Vec<_>>()
3326 .join("\n");
3327 let yaml = YamlFile::from_str(&yaml_str).unwrap();
3328 let doc = yaml.document().unwrap();
3329 let mapping = doc.as_mapping().unwrap();
3330
3331 assert_eq!(mapping.len(), 100);
3332 mapping.clear();
3333 assert_eq!(mapping.len(), 0);
3334 assert!(mapping.is_empty());
3335 }
3336
3337 #[test]
3338 fn test_mapping_newline_handling_block_style() {
3339 let yaml_with_newline = "key1: value1\nkey2: value2\n";
3341 let yaml = YamlFile::from_str(yaml_with_newline).unwrap();
3342
3343 let output = yaml.to_string();
3345 assert!(
3346 output.ends_with('\n'),
3347 "Block-style mapping should preserve trailing newline"
3348 );
3349 assert_eq!(output, yaml_with_newline);
3350 }
3351
3352 #[test]
3353 fn test_mapping_newline_handling_no_trailing() {
3354 let yaml_no_newline = "key: value";
3356 let yaml = YamlFile::from_str(yaml_no_newline).unwrap();
3357
3358 let output = yaml.to_string();
3360 assert!(
3361 !output.ends_with('\n'),
3362 "Mapping without trailing newline should not add one"
3363 );
3364 assert_eq!(output, yaml_no_newline);
3365 }
3366
3367 #[test]
3368 fn test_mapping_newline_handling_flow_style() {
3369 let yaml_flow = "data: {key1: value1, key2: value2}";
3371 let yaml = YamlFile::from_str(yaml_flow).unwrap();
3372
3373 let output = yaml.to_string();
3375 assert_eq!(output, yaml_flow);
3376 }
3377
3378 #[test]
3379 fn test_mapping_set_preserves_newline_context() {
3380 let yaml_str = "key1: value1\nkey2: value2\n";
3382 let yaml = YamlFile::from_str(yaml_str).unwrap();
3383 let doc = yaml.document().unwrap();
3384 let mapping = doc.as_mapping().unwrap();
3385
3386 mapping.set("key1", "new_value");
3388
3389 let output = yaml.to_string();
3391 assert!(
3392 output.ends_with('\n'),
3393 "Newline should be preserved after modification"
3394 );
3395 }
3396}