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
17fn fresh_token(kind: SyntaxKind, text: &str) -> rowan::SyntaxToken<Lang> {
20 let mut builder = GreenNodeBuilder::new();
21 builder.start_node(SyntaxKind::ROOT.into());
22 builder.token(kind.into(), text);
23 builder.finish_node();
24 SyntaxNode::new_root_mut(builder.finish())
25 .first_token()
26 .expect("just built a token")
27}
28
29fn find_key_line_comment(entry: &SyntaxNode) -> Option<(String, String)> {
40 let mut seen_colon = false;
41 let mut last_ws: Option<String> = None;
42 for tok in entry
43 .descendants_with_tokens()
44 .filter_map(|c| c.into_token())
45 {
46 match tok.kind() {
47 SyntaxKind::COLON if !seen_colon => {
48 seen_colon = true;
49 last_ws = None;
50 }
51 _ if !seen_colon => {}
52 SyntaxKind::WHITESPACE => last_ws = Some(tok.text().to_string()),
53 SyntaxKind::COMMENT => {
54 return Some((
55 last_ws.unwrap_or_else(|| " ".to_string()),
56 tok.text().to_string(),
57 ));
58 }
59 SyntaxKind::NEWLINE => return None,
60 _ => last_ws = None,
61 }
62 }
63 None
64}
65
66fn is_block_scalar(source: &SyntaxNode) -> bool {
70 source.kind() == SyntaxKind::SCALAR
71 && source
72 .first_token()
73 .is_some_and(|t| matches!(t.kind(), SyntaxKind::PIPE | SyntaxKind::GREATER))
74}
75
76fn build_block_value_node(source: &SyntaxNode, target: usize) -> (SyntaxNode, bool) {
88 if source.kind() == SyntaxKind::TAGGED_NODE {
89 return build_tagged_value_node(source, target);
90 }
91 let is_scalar = is_block_scalar(source);
95 let source_base = if is_scalar {
96 block_scalar_content_indent(source).unwrap_or(0)
97 } else {
98 crate::as_yaml::source_base_indent(source)
99 };
100 let delta = target as isize - source_base as isize;
101 let anchor = source_anchor_text(source);
102
103 let mut value_builder = GreenNodeBuilder::new();
104 value_builder.start_node(SyntaxKind::VALUE.into());
105 if let Some(a) = anchor.as_deref() {
107 value_builder.token(SyntaxKind::WHITESPACE.into(), " ");
108 value_builder.token(SyntaxKind::ANCHOR.into(), a);
109 }
110 if is_scalar {
114 value_builder.token(SyntaxKind::WHITESPACE.into(), " ");
115 } else {
116 value_builder.token(SyntaxKind::NEWLINE.into(), "\n");
117 value_builder.token(SyntaxKind::INDENT.into(), &" ".repeat(target));
118 }
119 value_builder.start_node(source.kind().into());
120 let ends_with_newline =
121 crate::as_yaml::copy_node_content_reindent(&mut value_builder, source, delta);
122 value_builder.finish_node();
123 value_builder.finish_node();
124 (
125 SyntaxNode::new_root_mut(value_builder.finish()),
126 ends_with_newline,
127 )
128}
129
130fn node_is_inline(node: &SyntaxNode) -> bool {
132 match node.kind() {
133 SyntaxKind::MAPPING => {
134 Mapping::cast(node.clone()).is_some_and(|m| ValueNode::is_inline(&m))
135 }
136 SyntaxKind::SEQUENCE => {
137 Sequence::cast(node.clone()).is_some_and(|s| ValueNode::is_inline(&s))
138 }
139 SyntaxKind::SCALAR => Scalar::cast(node.clone()).is_some_and(|s| ValueNode::is_inline(&s)),
140 _ => true,
141 }
142}
143
144fn build_tagged_value_node(source: &SyntaxNode, target: usize) -> (SyntaxNode, bool) {
150 let tag_text = source
152 .children_with_tokens()
153 .filter_map(|c| c.into_token())
154 .find(|t| t.kind() == SyntaxKind::TAG)
155 .map(|t| t.text().to_string());
156 let inner = source.first_child();
157 let anchor = source_anchor_text(source);
158
159 let mut value_builder = GreenNodeBuilder::new();
160 value_builder.start_node(SyntaxKind::VALUE.into());
161 value_builder.token(SyntaxKind::WHITESPACE.into(), " ");
162 if let Some(a) = anchor.as_deref() {
163 value_builder.token(SyntaxKind::ANCHOR.into(), a);
164 value_builder.token(SyntaxKind::WHITESPACE.into(), " ");
165 }
166 value_builder.start_node(SyntaxKind::TAGGED_NODE.into());
167 if let Some(tag) = tag_text.as_deref() {
168 value_builder.token(SyntaxKind::TAG.into(), tag);
169 }
170 let ends_with_newline = match inner {
171 None => false,
172 Some(inner) if node_is_inline(&inner) => {
173 value_builder.token(SyntaxKind::WHITESPACE.into(), " ");
175 value_builder.start_node(inner.kind().into());
176 let n = crate::as_yaml::copy_node_content_reindent(&mut value_builder, &inner, 0);
177 value_builder.finish_node();
178 n
179 }
180 Some(inner) => {
181 value_builder.token(SyntaxKind::NEWLINE.into(), "\n");
183 value_builder.token(SyntaxKind::INDENT.into(), &" ".repeat(target));
184 let delta = target as isize - crate::as_yaml::source_base_indent(&inner) as isize;
185 value_builder.start_node(inner.kind().into());
186 let n = crate::as_yaml::copy_node_content_reindent(&mut value_builder, &inner, delta);
187 value_builder.finish_node();
188 n
189 }
190 };
191 value_builder.finish_node(); value_builder.finish_node(); (
194 SyntaxNode::new_root_mut(value_builder.finish()),
195 ends_with_newline,
196 )
197}
198
199fn source_anchor_text(source: &SyntaxNode) -> Option<String> {
202 let mut cursor = source.prev_sibling_or_token();
203 while let Some(item) = cursor {
204 let tok = item.as_token()?;
205 match tok.kind() {
206 SyntaxKind::ANCHOR => return Some(tok.text().to_string()),
207 SyntaxKind::WHITESPACE | SyntaxKind::INDENT | SyntaxKind::NEWLINE => {}
208 _ => return None,
209 }
210 cursor = item.prev_sibling_or_token();
211 }
212 None
213}
214
215fn block_scalar_content_indent(scalar: &SyntaxNode) -> Option<usize> {
219 let mut after_newline = false;
220 for child in scalar.children_with_tokens() {
221 if let Some(tok) = child.as_token() {
222 match tok.kind() {
223 SyntaxKind::NEWLINE => after_newline = true,
224 SyntaxKind::WHITESPACE | SyntaxKind::INDENT if after_newline => {
225 return Some(tok.text().len());
226 }
227 _ => after_newline = false,
228 }
229 }
230 }
231 None
232}
233
234impl MappingEntry {
235 #[cfg(test)]
237 pub(crate) fn syntax(&self) -> &SyntaxNode {
238 &self.0
239 }
240
241 pub(crate) fn key(&self) -> Option<SyntaxNode> {
249 self.0.children().find(|n| n.kind() == SyntaxKind::KEY)
250 }
251
252 pub fn key_matches(&self, key: impl crate::AsYaml) -> bool {
258 self.key().is_some_and(|k| key_content_matches(&k, key))
259 }
260
261 pub(crate) fn value(&self) -> Option<SyntaxNode> {
267 self.0.children().find(|n| n.kind() == SyntaxKind::VALUE)
268 }
269
270 pub fn key_node(&self) -> Option<crate::as_yaml::YamlNode> {
274 self.key()
275 .and_then(|k| k.children().next())
276 .and_then(crate::as_yaml::YamlNode::from_syntax)
277 }
278
279 pub fn value_node(&self) -> Option<crate::as_yaml::YamlNode> {
283 self.value()
284 .and_then(|v| v.children().next())
285 .and_then(crate::as_yaml::YamlNode::from_syntax)
286 }
287
288 pub fn new(
299 key: impl crate::AsYaml,
300 value: impl crate::AsYaml,
301 flow_context: bool,
302 use_explicit_key: bool,
303 ) -> Self {
304 Self::new_at_indent(key, value, flow_context, use_explicit_key, 0)
305 }
306
307 pub fn new_at_indent(
312 key: impl crate::AsYaml,
313 value: impl crate::AsYaml,
314 flow_context: bool,
315 use_explicit_key: bool,
316 key_indent: usize,
317 ) -> Self {
318 let mut builder = GreenNodeBuilder::new();
319 builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
320
321 if use_explicit_key {
322 builder.token(SyntaxKind::QUESTION.into(), "?");
324 builder.token(SyntaxKind::WHITESPACE.into(), " ");
325 }
326
327 builder.start_node(SyntaxKind::KEY.into());
329 let key_has_newline = key.build_content(&mut builder, 0, false);
330 debug_assert!(!key_has_newline, "Keys should not end with newlines");
331 builder.finish_node();
332
333 if use_explicit_key {
334 builder.token(SyntaxKind::NEWLINE.into(), "\n");
336 }
337
338 builder.token(SyntaxKind::COLON.into(), ":");
339
340 let block_source = (!flow_context && !value.is_inline())
352 .then(|| value.as_node())
353 .flatten()
354 .filter(|_| {
355 matches!(
356 value.kind(),
357 crate::as_yaml::YamlKind::Mapping
358 | crate::as_yaml::YamlKind::Sequence
359 | crate::as_yaml::YamlKind::Scalar
360 | crate::as_yaml::YamlKind::Tagged(_)
361 )
362 })
363 .filter(|src| {
368 key_indent > 0
369 || crate::as_yaml::source_base_indent(src) > 0
370 || src.children().next().is_none()
371 });
372
373 let value_ends_with_newline = if let Some(source) = block_source {
374 let (value_node, ends_with_newline) = build_block_value_node(source, key_indent + 2);
375 builder.start_node(value_node.kind().into());
376 add_node_children_to(&mut builder, &value_node);
377 builder.finish_node();
378 ends_with_newline
379 } else {
380 builder.start_node(SyntaxKind::VALUE.into());
384 let ends_with_newline = match (value.is_inline(), value.kind()) {
385 (true, _) => {
387 builder.token(SyntaxKind::WHITESPACE.into(), " ");
388 value.build_content(&mut builder, 0, flow_context)
391 }
392 (false, crate::as_yaml::YamlKind::Mapping)
395 | (false, crate::as_yaml::YamlKind::Sequence) => {
396 builder.token(SyntaxKind::NEWLINE.into(), "\n");
397 value.build_content(&mut builder, 0, flow_context)
398 }
399 (false, _) => {
401 builder.token(SyntaxKind::NEWLINE.into(), "\n");
402 builder.token(SyntaxKind::INDENT.into(), " ");
403 value.build_content(&mut builder, 2, flow_context)
404 }
405 };
406 builder.finish_node(); ends_with_newline
408 };
409
410 if !value_ends_with_newline {
412 builder.token(SyntaxKind::NEWLINE.into(), "\n");
413 }
414
415 builder.finish_node(); MappingEntry(SyntaxNode::new_root_mut(builder.finish()))
417 }
418
419 fn indent_of_line(&self) -> usize {
424 crate::as_yaml::source_base_indent(&self.0)
425 }
426
427 fn value_index(&self) -> Option<usize> {
429 self.0
430 .children_with_tokens()
431 .position(|c| c.as_node().is_some_and(|n| n.kind() == SyntaxKind::VALUE))
432 }
433
434 fn insert_at(&self, at: usize, tokens: Vec<rowan::SyntaxToken<Lang>>) {
436 let elems: Vec<_> = tokens.into_iter().map(Into::into).collect();
437 self.0.splice_children(at..at, elems);
438 }
439
440 fn append(&self, tokens: Vec<rowan::SyntaxToken<Lang>>) {
442 let end = self.0.children_with_tokens().count();
443 self.insert_at(end, tokens);
444 }
445
446 fn detach_last_if_newline(&self) {
448 if let Some(last) = self.0.last_child_or_token() {
449 if last
450 .as_token()
451 .is_some_and(|t| t.kind() == SyntaxKind::NEWLINE)
452 {
453 last.detach();
454 }
455 }
456 }
457
458 fn ends_with_newline(&self) -> bool {
460 self.0
461 .last_token()
462 .is_some_and(|t| t.kind() == SyntaxKind::NEWLINE)
463 }
464
465 fn set_block_value(&self, source: &SyntaxNode) {
475 let (new_value_node, value_ends_with_newline) =
476 build_block_value_node(source, self.indent_of_line() + 2);
477
478 let old_entry_owned_trailing_nl = self.ends_with_newline();
483
484 let Some(value_idx) = self.value_index() else {
485 return;
486 };
487 let trailing_comment = find_key_line_comment(&self.0);
488 let ws_between = self
492 .0
493 .children_with_tokens()
494 .nth(value_idx.wrapping_sub(1))
495 .and_then(|c| c.into_token())
496 .filter(|t| t.kind() == SyntaxKind::WHITESPACE);
497
498 self.0
499 .splice_children(value_idx..value_idx + 1, vec![new_value_node.into()]);
500 if let Some(ws) = ws_between {
502 ws.detach();
503 }
504 if let Some((ws, comment)) = trailing_comment {
507 if let Some(idx) = self.value_index() {
508 self.insert_at(
509 idx,
510 vec![
511 fresh_token(SyntaxKind::WHITESPACE, &ws),
512 fresh_token(SyntaxKind::COMMENT, &comment),
513 ],
514 );
515 }
516 }
517
518 if value_ends_with_newline {
522 self.detach_last_if_newline();
525 } else if !self.ends_with_newline() {
526 self.append(vec![fresh_token(SyntaxKind::NEWLINE, "\n")]);
529 }
530 let entry_ends_with_nl = self.ends_with_newline();
531 if entry_ends_with_nl && !old_entry_owned_trailing_nl {
532 if let Some(next) = self.0.next_sibling_or_token() {
533 if next
534 .as_token()
535 .is_some_and(|t| t.kind() == SyntaxKind::NEWLINE)
536 {
537 next.detach();
538 }
539 }
540 }
541 }
542
543 pub fn set_value(&self, new_value: impl crate::AsYaml, flow_context: bool) {
545 use crate::as_yaml::YamlKind;
546
547 let block_source = (!flow_context && !new_value.is_inline())
555 .then(|| new_value.as_node())
556 .flatten()
557 .filter(|_| {
558 matches!(
559 new_value.kind(),
560 YamlKind::Mapping | YamlKind::Sequence | YamlKind::Scalar | YamlKind::Tagged(_)
561 )
562 });
563 if let Some(source) = block_source {
564 self.set_block_value(source);
565 return;
566 }
567
568 let anchor = new_value.as_node().and_then(source_anchor_text);
576 let trailing_comment = find_key_line_comment(&self.0);
577 let mut value_builder = GreenNodeBuilder::new();
578 value_builder.start_node(SyntaxKind::VALUE.into());
579 if let Some(a) = anchor.as_deref() {
580 value_builder.token(SyntaxKind::ANCHOR.into(), a);
581 value_builder.token(SyntaxKind::WHITESPACE.into(), " ");
582 }
583 new_value.build_content(&mut value_builder, 0, flow_context);
584 if let Some((ws, comment)) = &trailing_comment {
585 value_builder.token(SyntaxKind::WHITESPACE.into(), ws);
586 value_builder.token(SyntaxKind::COMMENT.into(), comment);
587 }
588 value_builder.finish_node();
589 let new_value_node = SyntaxNode::new_root_mut(value_builder.finish());
590
591 let old_was_block = self.value().is_some_and(|v| v.text().contains_char('\n'));
592 if old_was_block {
593 self.rebuild_with_inline_value(&new_value_node);
594 return;
595 }
596
597 if let Some(value_idx) = self
600 .0
601 .children_with_tokens()
602 .position(|c| c.as_node().is_some_and(|n| n.kind() == SyntaxKind::VALUE))
603 {
604 self.0
605 .splice_children(value_idx..value_idx + 1, vec![new_value_node.into()]);
606 return;
607 }
608
609 if let Some(colon_idx) = self
612 .0
613 .children_with_tokens()
614 .position(|c| c.as_token().is_some_and(|t| t.kind() == SyntaxKind::COLON))
615 {
616 let insert_at = colon_idx + 1;
617 let ws = fresh_token(SyntaxKind::WHITESPACE, " ");
618 self.0
619 .splice_children(insert_at..insert_at, vec![ws.into(), new_value_node.into()]);
620 }
621 }
622
623 fn rebuild_with_inline_value(&self, new_value_node: &SyntaxNode) {
630 let Some(value_idx) = self.value_index() else {
631 return;
632 };
633
634 let stale: Vec<_> = self
640 .0
641 .children_with_tokens()
642 .take(value_idx)
643 .filter_map(|c| c.into_token())
644 .filter(|t| matches!(t.kind(), SyntaxKind::WHITESPACE | SyntaxKind::COMMENT))
645 .collect();
646 if stale.iter().any(|t| t.kind() == SyntaxKind::COMMENT) {
647 for tok in stale {
648 tok.detach();
649 }
650 }
651
652 let value_idx = self.value_index().expect("VALUE was here a moment ago");
654 self.0.splice_children(
655 value_idx..value_idx + 1,
656 vec![new_value_node.clone().into()],
657 );
658
659 if let Some(idx) = self.value_index() {
662 let prev_is_ws = idx.checked_sub(1).is_some_and(|p| {
663 self.0
664 .children_with_tokens()
665 .nth(p)
666 .and_then(|c| c.into_token())
667 .is_some_and(|t| t.kind() == SyntaxKind::WHITESPACE)
668 });
669 if !prev_is_ws {
670 self.insert_at(idx, vec![fresh_token(SyntaxKind::WHITESPACE, " ")]);
671 }
672 }
673
674 if !self.ends_with_newline() {
677 self.append(vec![fresh_token(SyntaxKind::NEWLINE, "\n")]);
678 }
679 }
680
681 pub fn discard(self) {
687 self.0.detach();
688 }
689
690 pub fn remove(self) {
699 self.discard();
700 }
701}
702
703ast_node!(Mapping, MAPPING, "A YAML mapping (key-value pairs)");
704
705impl Mapping {
706 pub fn dump_cst(&self) -> String {
711 dump_cst_to_string(&self.0, 0)
712 }
713
714 pub fn keys(&self) -> impl Iterator<Item = crate::as_yaml::YamlNode> + '_ {
726 self.pairs().filter_map(|(k, _)| {
727 k.children()
728 .next()
729 .and_then(crate::as_yaml::YamlNode::from_syntax)
730 })
731 }
732
733 pub(crate) fn pairs(&self) -> impl Iterator<Item = (SyntaxNode, SyntaxNode)> + '_ {
745 self.0
746 .children()
747 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
748 .filter_map(|entry| {
749 let key = entry.children().find(|n| n.kind() == SyntaxKind::KEY)?;
750 let value = entry.children().find(|n| n.kind() == SyntaxKind::VALUE)?;
751 Some((key, value))
752 })
753 }
754
755 pub fn get(&self, key: impl crate::AsYaml) -> Option<crate::as_yaml::YamlNode> {
759 self.get_node(key)
760 .and_then(crate::as_yaml::YamlNode::from_syntax)
761 }
762
763 pub(crate) fn get_node(&self, key: impl crate::AsYaml) -> Option<SyntaxNode> {
769 self.find_entry_by_key(key)
770 .and_then(|entry| entry.value())
771 .and_then(|value_node| {
772 value_node.children().next()
774 })
775 }
776
777 pub fn get_mapping(&self, key: impl crate::AsYaml) -> Option<Mapping> {
781 self.get(key).and_then(|n| n.as_mapping().cloned())
782 }
783
784 pub fn modify_mapping<F>(&self, key: impl crate::AsYaml, f: F) -> bool
794 where
795 F: FnOnce(&Mapping),
796 {
797 let children: Vec<_> = self.0.children_with_tokens().collect();
799 for (i, child) in children.iter().enumerate() {
800 if let Some(node) = child.as_node() {
801 if node.kind() == SyntaxKind::MAPPING_ENTRY {
802 if let Some(key_node) = node.children().find(|n| n.kind() == SyntaxKind::KEY) {
803 if key_content_matches(&key_node, &key) {
804 if let Some(value_node) =
806 node.children().find(|n| n.kind() == SyntaxKind::VALUE)
807 {
808 if let Some(mapping_node) = value_node
810 .children()
811 .find(|n| n.kind() == SyntaxKind::MAPPING)
812 {
813 let mapping = Mapping(mapping_node);
815 f(&mapping);
816
817 let entry_children: Vec<_> =
819 node.children_with_tokens().collect();
820 let mut builder = GreenNodeBuilder::new();
821 builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
822
823 for entry_child in entry_children {
824 match entry_child {
825 rowan::NodeOrToken::Node(n)
826 if n.kind() == SyntaxKind::VALUE =>
827 {
828 builder.start_node(SyntaxKind::VALUE.into());
830
831 for value_child in n.children_with_tokens() {
833 match value_child {
834 rowan::NodeOrToken::Node(child_node)
835 if child_node.kind()
836 == SyntaxKind::MAPPING =>
837 {
838 crate::yaml::copy_node_to_builder(
840 &mut builder,
841 &mapping.0,
842 );
843 }
844 rowan::NodeOrToken::Node(child_node) => {
845 crate::yaml::copy_node_to_builder(
847 &mut builder,
848 &child_node,
849 );
850 }
851 rowan::NodeOrToken::Token(token) => {
852 builder.token(
854 token.kind().into(),
855 token.text(),
856 );
857 }
858 }
859 }
860
861 builder.finish_node(); }
863 rowan::NodeOrToken::Node(n) => {
864 crate::yaml::copy_node_to_builder(&mut builder, &n);
865 }
866 rowan::NodeOrToken::Token(t) => {
867 builder.token(t.kind().into(), t.text());
868 }
869 }
870 }
871
872 builder.finish_node(); let new_entry = SyntaxNode::new_root_mut(builder.finish());
874 self.0.splice_children(i..i + 1, vec![new_entry.into()]);
875 return true;
876 }
877 }
878 }
879 }
880 }
881 }
882 }
883 false
884 }
885
886 pub fn get_sequence(&self, key: impl crate::AsYaml) -> Option<Sequence> {
890 self.get(key).and_then(|n| n.as_sequence().cloned())
891 }
892
893 pub fn contains_key(&self, key: impl crate::AsYaml) -> bool {
895 self.find_entry_by_key(key).is_some()
896 }
897
898 pub(crate) fn key_nodes(&self) -> impl Iterator<Item = SyntaxNode> + '_ {
905 self.pairs().map(|(k, _)| k)
906 }
907
908 pub fn is_empty(&self) -> bool {
910 self.pairs().next().is_none()
911 }
912
913 pub fn len(&self) -> usize {
915 self.pairs().count()
916 }
917
918 pub fn values(&self) -> impl Iterator<Item = crate::as_yaml::YamlNode> + '_ {
925 self.pairs().filter_map(|(_, value_node)| {
926 value_node
928 .children()
929 .next()
930 .and_then(crate::as_yaml::YamlNode::from_syntax)
931 })
932 }
933
934 pub fn iter(
940 &self,
941 ) -> impl Iterator<Item = (crate::as_yaml::YamlNode, crate::as_yaml::YamlNode)> + '_ {
942 self.pairs().filter_map(|(key_node, value_node)| {
943 let key = key_node
945 .children()
946 .next()
947 .and_then(crate::as_yaml::YamlNode::from_syntax)?;
948 let value = value_node
949 .children()
950 .next()
951 .and_then(crate::as_yaml::YamlNode::from_syntax)?;
952 Some((key, value))
953 })
954 }
955
956 pub fn new() -> Self {
958 let mut builder = GreenNodeBuilder::new();
959 builder.start_node(SyntaxKind::MAPPING.into());
960 builder.finish_node();
961 Mapping(SyntaxNode::new_root_mut(builder.finish()))
962 }
963
964 pub fn reorder_fields<I, K>(&self, order: I)
969 where
970 I: IntoIterator<Item = K>,
971 K: crate::AsYaml,
972 {
973 let order_keys: Vec<K> = order.into_iter().collect();
974
975 let entry_nodes: Vec<SyntaxNode> = self
977 .0
978 .children()
979 .filter(|child| child.kind() == SyntaxKind::MAPPING_ENTRY)
980 .collect();
981
982 let mut ordered_entries: Vec<SyntaxNode> = Vec::new();
984 let mut remaining_entries: Vec<SyntaxNode> = entry_nodes;
985
986 for order_key in &order_keys {
987 if let Some(pos) = remaining_entries.iter().position(|entry| {
988 entry
989 .children()
990 .find(|n| n.kind() == SyntaxKind::KEY)
991 .map(|k| key_content_matches(&k, order_key))
992 .unwrap_or(false)
993 }) {
994 ordered_entries.push(remaining_entries.remove(pos));
995 }
996 }
997
998 let new_children: Vec<_> = ordered_entries
999 .into_iter()
1000 .chain(remaining_entries)
1001 .map(|node| node.into())
1002 .collect();
1003
1004 let children_count = self.0.children_with_tokens().count();
1006 self.0.splice_children(0..children_count, new_children);
1007 }
1008}
1009
1010fn key_content_matches(key_node: &SyntaxNode, key: impl crate::AsYaml) -> bool {
1014 match crate::as_yaml::YamlNode::from_syntax_peeled(key_node.clone()) {
1015 Some(node) => crate::as_yaml::yaml_eq(&node, &key),
1016 None => false,
1017 }
1018}
1019
1020impl Mapping {
1021 pub fn is_flow_style(&self) -> bool {
1026 self.0.children_with_tokens().any(|child| {
1028 child
1029 .as_token()
1030 .map(|token| token.kind() == SyntaxKind::LEFT_BRACE)
1031 .unwrap_or(false)
1032 })
1033 }
1034
1035 pub fn find_entry_by_key(&self, key: impl crate::AsYaml) -> Option<MappingEntry> {
1040 self.0
1041 .children()
1042 .filter_map(MappingEntry::cast)
1043 .find(|entry| entry.key().is_some_and(|k| key_content_matches(&k, &key)))
1044 }
1045
1046 pub fn find_all_entries_by_key<'a>(
1077 &'a self,
1078 key: impl crate::AsYaml + 'a,
1079 ) -> impl Iterator<Item = MappingEntry> + 'a {
1080 self.0
1081 .children()
1082 .filter_map(MappingEntry::cast)
1083 .filter(move |entry| entry.key().is_some_and(|k| key_content_matches(&k, &key)))
1084 }
1085
1086 pub fn entries(&self) -> impl Iterator<Item = MappingEntry> {
1092 self.0.children().filter_map(MappingEntry::cast)
1093 }
1094
1095 pub fn find_entry_index_by_key(&self, key: impl crate::AsYaml) -> Option<usize> {
1101 self.0
1103 .children_with_tokens()
1104 .enumerate()
1105 .find_map(|(i, child)| {
1106 let node = child.as_node()?;
1107 if node.kind() != SyntaxKind::MAPPING_ENTRY {
1108 return None;
1109 }
1110 let entry = MappingEntry::cast(node.clone())?;
1111 if entry.key().is_some_and(|k| key_content_matches(&k, &key)) {
1112 Some(i)
1113 } else {
1114 None
1115 }
1116 })
1117 }
1118
1119 pub fn set(&self, key: impl crate::AsYaml, value: impl crate::AsYaml) {
1129 self.set_as_yaml(key, value);
1130 }
1131
1132 fn uses_explicit_keys(&self) -> bool {
1134 for child in self.0.children() {
1137 if child.kind() == SyntaxKind::MAPPING_ENTRY {
1138 if child.children_with_tokens().any(|t| {
1140 t.as_token()
1141 .is_some_and(|tok| tok.kind() == SyntaxKind::QUESTION)
1142 }) {
1143 return true;
1144 }
1145 }
1146 }
1147 false
1148 }
1149
1150 fn set_as_yaml<K: crate::AsYaml, V: crate::AsYaml>(&self, key: K, value: V) {
1152 let flow_context = self.is_flow_style();
1154
1155 let use_explicit_keys = self.uses_explicit_keys();
1157
1158 for (i, child) in self.0.children_with_tokens().enumerate() {
1160 if let Some(node) = child.as_node() {
1161 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1162 if let Some(entry) = MappingEntry::cast(node.clone()) {
1163 if let Some(entry_key_node) = entry.key() {
1165 if key_content_matches(&entry_key_node, &key) {
1166 entry.set_value(value, flow_context);
1168
1169 self.0.splice_children(i..i + 1, vec![entry.0.into()]);
1170 return;
1171 }
1172 }
1173 }
1174 }
1175 }
1176 }
1177
1178 let key_indent = self.detect_indentation_level();
1182 let new_entry =
1183 MappingEntry::new_at_indent(key, value, flow_context, use_explicit_keys, key_indent);
1184 self.insert_entry_cst(&new_entry.0);
1185 }
1186
1187 fn insert_entry_cst(&self, new_entry: &SyntaxNode) {
1189 let mut count = 0;
1191 let mut last_mapping_entry: Option<SyntaxNode> = None;
1192
1193 for child in self.0.children_with_tokens() {
1194 count += 1;
1195 if let Some(node) = child.as_node() {
1196 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1197 last_mapping_entry = Some(node.clone());
1198 }
1199 }
1200 }
1201
1202 let has_trailing_newline = if let Some(entry) = &last_mapping_entry {
1205 entry
1206 .last_token()
1207 .map(|t| t.kind() == SyntaxKind::NEWLINE)
1208 .unwrap_or(false)
1209 } else {
1210 self.0
1212 .last_token()
1213 .map(|t| t.kind() == SyntaxKind::NEWLINE)
1214 .unwrap_or(false)
1215 };
1216
1217 let mut new_elements = Vec::new();
1218
1219 let insert_pos = count;
1221
1222 let indent_level = self.detect_indentation_level();
1224 if indent_level > 0 && count > 0 {
1225 let mut builder = rowan::GreenNodeBuilder::new();
1226 builder.start_node(SyntaxKind::ROOT.into());
1227 if !has_trailing_newline {
1229 builder.token(SyntaxKind::NEWLINE.into(), "\n");
1230 }
1231 builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent_level));
1232 builder.finish_node();
1233 let node = SyntaxNode::new_root_mut(builder.finish());
1234 for child in node.children_with_tokens() {
1236 if let rowan::NodeOrToken::Token(token) = child {
1237 new_elements.push(token.into());
1238 }
1239 }
1240 } else if count > 0 && !has_trailing_newline {
1241 let mut builder = rowan::GreenNodeBuilder::new();
1243 builder.start_node(SyntaxKind::ROOT.into());
1244 builder.token(SyntaxKind::NEWLINE.into(), "\n");
1245 builder.finish_node();
1246 let node = SyntaxNode::new_root_mut(builder.finish());
1247 if let Some(token) = node.first_token() {
1248 new_elements.push(token.into());
1249 }
1250 }
1251
1252 new_elements.push(new_entry.clone().into());
1253
1254 self.0.splice_children(insert_pos..insert_pos, new_elements);
1258
1259 if count == 0 {
1266 let entry_ends_with_nl = new_entry
1267 .last_token()
1268 .is_some_and(|t| t.kind() == SyntaxKind::NEWLINE);
1269 if entry_ends_with_nl {
1270 if let Some(parent_value) = self.0.parent() {
1271 if parent_value.kind() == SyntaxKind::VALUE {
1272 if let Some(parent_entry) = parent_value.parent() {
1273 if parent_entry.kind() == SyntaxKind::MAPPING_ENTRY {
1274 if let Some(last) = parent_entry.last_child_or_token() {
1275 if last
1276 .as_token()
1277 .is_some_and(|t| t.kind() == SyntaxKind::NEWLINE)
1278 {
1279 last.detach();
1280 }
1281 }
1282 }
1283 }
1284 }
1285 }
1286 }
1287 }
1288 }
1289
1290 pub(crate) fn compare_key_nodes(&self, actual: &SyntaxNode, expected: &SyntaxNode) -> bool {
1292 if actual.kind() != SyntaxKind::KEY || expected.kind() != SyntaxKind::KEY {
1294 return actual.kind() == expected.kind()
1295 && self.compare_nodes_structurally(actual, expected);
1296 }
1297
1298 let actual_content = self.get_key_content_nodes(actual);
1300 let expected_content = self.get_key_content_nodes(expected);
1301
1302 if actual_content.len() != expected_content.len() {
1303 return false;
1304 }
1305
1306 for (a, e) in actual_content.iter().zip(expected_content.iter()) {
1307 if !self.compare_nodes_structurally(a, e) {
1308 return false;
1309 }
1310 }
1311
1312 true
1313 }
1314
1315 fn get_key_content_nodes(&self, key_node: &SyntaxNode) -> Vec<SyntaxNode> {
1317 let mut nodes = Vec::new();
1318 for child in key_node.children_with_tokens() {
1319 match child {
1320 rowan::NodeOrToken::Node(n) => {
1321 nodes.push(n);
1323 }
1324 rowan::NodeOrToken::Token(t) => {
1325 if t.kind() != SyntaxKind::WHITESPACE
1327 && t.kind() != SyntaxKind::INDENT
1328 && t.kind() != SyntaxKind::QUESTION
1329 {
1330 let mut token_builder = GreenNodeBuilder::new();
1332 token_builder.start_node(t.kind().into());
1333 token_builder.token(t.kind().into(), t.text());
1334 token_builder.finish_node();
1335 nodes.push(SyntaxNode::new_root_mut(token_builder.finish()));
1336 }
1337 }
1338 }
1339 }
1340 nodes
1341 }
1342
1343 fn compare_nodes_structurally(&self, node1: &SyntaxNode, node2: &SyntaxNode) -> bool {
1345 if node1.kind() != node2.kind() {
1346 return false;
1347 }
1348
1349 match node1.kind() {
1350 SyntaxKind::SCALAR => {
1351 let s1 = Scalar::cast(node1.clone()).map(|s| s.as_string());
1355 let s2 = Scalar::cast(node2.clone()).map(|s| s.as_string());
1356 s1 == s2 && s1.is_some()
1357 }
1358 SyntaxKind::STRING => {
1359 let mut iter1 = node1
1361 .children_with_tokens()
1362 .filter_map(|c| c.into_token())
1363 .filter(|t| t.kind() == SyntaxKind::STRING);
1364 let mut iter2 = node2
1365 .children_with_tokens()
1366 .filter_map(|c| c.into_token())
1367 .filter(|t| t.kind() == SyntaxKind::STRING);
1368 loop {
1369 match (iter1.next(), iter2.next()) {
1370 (Some(a), Some(b)) if a.text() == b.text() => continue,
1371 (None, None) => return true,
1372 _ => return false,
1373 }
1374 }
1375 }
1376 SyntaxKind::SEQUENCE => {
1377 let mut entries1 = node1
1379 .children()
1380 .filter(|n| n.kind() == SyntaxKind::SEQUENCE_ENTRY);
1381 let mut entries2 = node2
1382 .children()
1383 .filter(|n| n.kind() == SyntaxKind::SEQUENCE_ENTRY);
1384 loop {
1385 match (entries1.next(), entries2.next()) {
1386 (Some(e1), Some(e2)) => {
1387 if !self.compare_sequence_entries(&e1, &e2) {
1388 return false;
1389 }
1390 }
1391 (None, None) => return true,
1392 _ => return false,
1393 }
1394 }
1395 }
1396 SyntaxKind::MAPPING => {
1397 let mut entries1 = node1
1399 .children()
1400 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY);
1401 let mut entries2 = node2
1402 .children()
1403 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY);
1404 loop {
1405 match (entries1.next(), entries2.next()) {
1406 (Some(e1), Some(e2)) => {
1407 if !self.compare_mapping_entries(&e1, &e2) {
1408 return false;
1409 }
1410 }
1411 (None, None) => return true,
1412 _ => return false,
1413 }
1414 }
1415 }
1416 _ => {
1417 let filter_tokens = |node: &SyntaxNode| {
1419 node.children_with_tokens()
1420 .filter_map(|c| c.into_token())
1421 .filter(|t| {
1422 t.kind() != SyntaxKind::WHITESPACE && t.kind() != SyntaxKind::INDENT
1423 })
1424 };
1425 let mut iter1 = filter_tokens(node1);
1426 let mut iter2 = filter_tokens(node2);
1427 loop {
1428 match (iter1.next(), iter2.next()) {
1429 (Some(a), Some(b)) if a.kind() == b.kind() && a.text() == b.text() => {
1430 continue
1431 }
1432 (None, None) => return true,
1433 _ => return false,
1434 }
1435 }
1436 }
1437 }
1438 }
1439
1440 fn compare_sequence_entries(&self, entry1: &SyntaxNode, entry2: &SyntaxNode) -> bool {
1442 let value1 = entry1.children().find(|n| n.kind() == SyntaxKind::VALUE);
1443 let value2 = entry2.children().find(|n| n.kind() == SyntaxKind::VALUE);
1444
1445 match (value1, value2) {
1446 (Some(v1), Some(v2)) => self.compare_nodes_structurally(&v1, &v2),
1447 (None, None) => true,
1448 _ => false,
1449 }
1450 }
1451
1452 fn compare_mapping_entries(&self, entry1: &SyntaxNode, entry2: &SyntaxNode) -> bool {
1454 let key1 = entry1.children().find(|n| n.kind() == SyntaxKind::KEY);
1455 let key2 = entry2.children().find(|n| n.kind() == SyntaxKind::KEY);
1456 let value1 = entry1.children().find(|n| n.kind() == SyntaxKind::VALUE);
1457 let value2 = entry2.children().find(|n| n.kind() == SyntaxKind::VALUE);
1458
1459 match ((key1, value1), (key2, value2)) {
1460 ((Some(k1), Some(v1)), (Some(k2), Some(v2))) => {
1461 self.compare_key_nodes(&k1, &k2) && self.compare_nodes_structurally(&v1, &v2)
1462 }
1463 ((Some(k1), None), (Some(k2), None)) => self.compare_key_nodes(&k1, &k2),
1464 ((None, Some(v1)), (None, Some(v2))) => self.compare_nodes_structurally(&v1, &v2),
1465 ((None, None), (None, None)) => true,
1466 _ => false,
1467 }
1468 }
1469
1470 pub fn set_with_field_order<I, K>(
1475 &self,
1476 key: impl crate::AsYaml,
1477 value: impl crate::AsYaml,
1478 field_order: I,
1479 ) where
1480 I: IntoIterator<Item = K>,
1481 K: crate::AsYaml,
1482 {
1483 let field_order: Vec<K> = field_order.into_iter().collect();
1485
1486 let children: Vec<_> = self.0.children_with_tokens().collect();
1488 for child in children.iter() {
1489 if let Some(node) = child.as_node() {
1490 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1491 if let Some(key_node) = node.children().find(|n| n.kind() == SyntaxKind::KEY) {
1492 if key_content_matches(&key_node, &key) {
1493 self.set_as_yaml(&key, &value);
1495 return;
1496 }
1497 }
1498 }
1499 }
1500 }
1501
1502 let key_position_in_order = field_order
1505 .iter()
1506 .position(|field| crate::as_yaml::yaml_eq(&key, field));
1507
1508 if let Some(key_index) = key_position_in_order {
1509 let mut insert_after_node: Option<SyntaxNode> = None;
1511 let mut insert_before_node: Option<SyntaxNode> = None;
1512
1513 for field in field_order.iter().take(key_index).rev() {
1515 for child in children.iter() {
1516 if let Some(node) = child.as_node() {
1517 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1518 if let Some(key_node) =
1519 node.children().find(|n| n.kind() == SyntaxKind::KEY)
1520 {
1521 if key_content_matches(&key_node, field) {
1522 insert_after_node = Some(node.clone());
1523 break;
1524 }
1525 }
1526 }
1527 }
1528 }
1529 if insert_after_node.is_some() {
1530 break;
1531 }
1532 }
1533
1534 if insert_after_node.is_none() {
1537 for child in children.iter() {
1538 if let Some(node) = child.as_node() {
1539 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1540 if let Some(existing_key_node) =
1541 node.children().find(|n| n.kind() == SyntaxKind::KEY)
1542 {
1543 let existing_key_position = field_order.iter().position(|field| {
1545 key_content_matches(&existing_key_node, field)
1546 });
1547
1548 if let Some(existing_pos) = existing_key_position {
1550 if existing_pos > key_index {
1551 insert_before_node = Some(node.clone());
1552 break;
1553 }
1554 }
1555 }
1556 }
1557 }
1558 }
1559 }
1560
1561 let flow_context = self.is_flow_style();
1563 let use_explicit_keys = self.uses_explicit_keys();
1564 let key_indent = self.detect_indentation_level();
1565 let new_entry = MappingEntry::new_at_indent(
1566 &key,
1567 &value,
1568 flow_context,
1569 use_explicit_keys,
1570 key_indent,
1571 )
1572 .0;
1573
1574 if let Some(after_node) = insert_after_node {
1576 let idx = children
1578 .iter()
1579 .position(|c| c.as_node() == Some(&after_node))
1580 .expect("after_node was found in children earlier");
1581
1582 let has_trailing_newline = after_node
1584 .last_token()
1585 .map(|t| t.kind() == SyntaxKind::NEWLINE)
1586 .unwrap_or(false);
1587
1588 if !has_trailing_newline {
1589 let entry_children_count = after_node.children_with_tokens().count();
1591 let mut nl_builder = GreenNodeBuilder::new();
1592 nl_builder.start_node(SyntaxKind::ROOT.into());
1593 nl_builder.token(SyntaxKind::NEWLINE.into(), "\n");
1594 nl_builder.finish_node();
1595 let nl_node = SyntaxNode::new_root_mut(nl_builder.finish());
1596 if let Some(token) = nl_node.first_token() {
1597 after_node.splice_children(
1598 entry_children_count..entry_children_count,
1599 vec![token.into()],
1600 );
1601 }
1602 }
1603
1604 self.0
1606 .splice_children(idx + 1..idx + 1, vec![new_entry.into()]);
1607 } else if let Some(before_node) = insert_before_node {
1608 let idx = children
1610 .iter()
1611 .position(|c| c.as_node() == Some(&before_node))
1612 .expect("before_node was found in children earlier");
1613
1614 if idx > 0 {
1616 if let Some(prev_entry) = children[..idx].iter().rev().find_map(|c| {
1617 c.as_node()
1618 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
1619 }) {
1620 let has_trailing_newline = prev_entry
1621 .last_token()
1622 .map(|t| t.kind() == SyntaxKind::NEWLINE)
1623 .unwrap_or(false);
1624
1625 if !has_trailing_newline {
1626 let entry_children_count = prev_entry.children_with_tokens().count();
1627 let mut nl_builder = GreenNodeBuilder::new();
1628 nl_builder.start_node(SyntaxKind::ROOT.into());
1629 nl_builder.token(SyntaxKind::NEWLINE.into(), "\n");
1630 nl_builder.finish_node();
1631 let nl_node = SyntaxNode::new_root_mut(nl_builder.finish());
1632 if let Some(token) = nl_node.first_token() {
1633 prev_entry.splice_children(
1634 entry_children_count..entry_children_count,
1635 vec![token.into()],
1636 );
1637 }
1638 }
1639 }
1640 }
1641
1642 self.0.splice_children(idx..idx, vec![new_entry.into()]);
1644 } else {
1645 self.set_as_yaml(&key, &value);
1647 }
1648 } else {
1649 self.set_as_yaml(&key, &value);
1651 }
1652 }
1653
1654 pub fn detect_indentation_level(&self) -> usize {
1658 for child in self.0.children_with_tokens() {
1661 if let Some(token) = child.as_token() {
1662 if token.kind() == SyntaxKind::INDENT {
1663 return token.text().len();
1664 }
1665 }
1666 }
1667 crate::as_yaml::source_base_indent(&self.0)
1671 }
1672
1673 pub fn move_after(
1685 &self,
1686 after_key: impl crate::AsYaml,
1687 new_key: impl crate::AsYaml,
1688 new_value: impl crate::AsYaml,
1689 ) -> bool {
1690 self.move_after_impl(after_key, new_key, new_value)
1691 }
1692
1693 fn move_after_impl(
1695 &self,
1696 after_key: impl crate::AsYaml,
1697 new_key: impl crate::AsYaml,
1698 new_value: impl crate::AsYaml,
1699 ) -> bool {
1700 let children: Vec<_> = self.0.children_with_tokens().collect();
1701 let mut insert_position = None;
1702 let mut found_key = false;
1703 let mut last_value_end = 0;
1704
1705 let mut i = 0;
1707 let mut removed_existing = false;
1708 while i < children.len() {
1709 if let Some(node) = children[i].as_node() {
1710 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1711 for key_child in node.children() {
1713 if key_child.kind() == SyntaxKind::KEY
1714 && key_content_matches(&key_child, &new_key)
1715 {
1716 let mut remove_range = i..i + 1;
1718
1719 if i + 1 < children.len() {
1721 if let Some(token) = children[i + 1].as_token() {
1722 if token.kind() == SyntaxKind::NEWLINE {
1723 remove_range = i..i + 2;
1724 }
1725 }
1726 }
1727
1728 self.0.splice_children(remove_range, vec![]);
1729 removed_existing = true;
1730 break;
1731 }
1732 }
1733 if removed_existing {
1734 break;
1736 }
1737 }
1738 }
1739 if !removed_existing {
1740 i += 1;
1741 }
1742 }
1743
1744 let children = if removed_existing {
1746 self.0.children_with_tokens().collect()
1747 } else {
1748 children
1749 };
1750
1751 for (i, child) in children.iter().enumerate() {
1753 if let Some(node) = child.as_node() {
1754 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1755 if found_key {
1756 let is_root_level = if i > 0 {
1759 children
1760 .get(i - 1)
1761 .and_then(|c| c.as_token())
1762 .map(|t| t.kind() != SyntaxKind::INDENT)
1763 .unwrap_or(true)
1764 } else {
1765 true
1766 };
1767
1768 if is_root_level {
1769 insert_position = Some(i);
1770 break;
1771 }
1772 }
1773 for key_child in node.children() {
1775 if key_child.kind() == SyntaxKind::KEY
1776 && key_content_matches(&key_child, &after_key)
1777 {
1778 found_key = true;
1779 last_value_end = i + 1; break;
1781 }
1782 }
1783 } else if node.kind() == SyntaxKind::KEY {
1784 if key_content_matches(node, &after_key) {
1785 found_key = true;
1786 }
1787 } else if node.kind() == SyntaxKind::SCALAR {
1788 if key_content_matches(node, &after_key) && !found_key {
1790 found_key = true;
1792 for (j, child_j) in children[(i + 1)..].iter().enumerate() {
1794 if let Some(n) = child_j.as_node() {
1795 if n.kind() == SyntaxKind::VALUE || n.kind() == SyntaxKind::SCALAR {
1796 last_value_end = i + 1 + j + 1;
1797 break;
1798 }
1799 }
1800 }
1801 }
1802 } else if node.kind() == SyntaxKind::VALUE && found_key {
1803 last_value_end = i + 1;
1805 }
1806 } else if let Some(token) = child.as_token() {
1807 if found_key && token.kind() == SyntaxKind::COMMENT {
1808 if i > 0 {
1813 if let Some(prev) = children.get(i - 1) {
1814 let is_top_level = if let Some(prev_token) = prev.as_token() {
1815 prev_token.kind() == SyntaxKind::NEWLINE
1817 } else if let Some(prev_node) = prev.as_node() {
1818 prev_node.kind() == SyntaxKind::MAPPING_ENTRY
1821 } else {
1822 false
1823 };
1824
1825 if is_top_level {
1826 insert_position = Some(i);
1828 break;
1829 }
1830 }
1831 }
1832 } else if found_key && token.kind() == SyntaxKind::NEWLINE {
1833 let is_root_level = if i > 0 {
1836 children
1837 .get(i - 1)
1838 .and_then(|c| c.as_token())
1839 .map(|t| t.kind() != SyntaxKind::INDENT)
1840 .unwrap_or(true)
1841 } else {
1842 true
1843 };
1844
1845 if is_root_level && i + 1 < children.len() {
1846 if let Some(next) = children.get(i + 1) {
1847 if let Some(next_token) = next.as_token() {
1848 if next_token.kind() == SyntaxKind::NEWLINE
1849 || next_token.kind() == SyntaxKind::COMMENT
1850 {
1851 insert_position = Some(i);
1856 break;
1857 }
1858 } else if next.as_node().is_some() {
1859 insert_position = Some(i);
1863 break;
1864 }
1865 }
1866 }
1867 }
1868 }
1869 }
1870
1871 if insert_position.is_none() && found_key && last_value_end > 0 {
1873 insert_position = Some(last_value_end);
1874 }
1875
1876 if let Some(pos) = insert_position {
1877 let mut new_elements = Vec::new();
1879
1880 if pos > 0 {
1882 if let Some(prev_entry) = children[..pos].iter().rev().find_map(|child| {
1884 child
1885 .as_node()
1886 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
1887 }) {
1888 let has_newline = prev_entry
1890 .last_token()
1891 .map(|t| t.kind() == SyntaxKind::NEWLINE)
1892 .unwrap_or(false);
1893
1894 if !has_newline {
1896 let entry_children_count = prev_entry.children_with_tokens().count();
1897 let mut nl_builder = GreenNodeBuilder::new();
1898 nl_builder.start_node(SyntaxKind::ROOT.into());
1899 nl_builder.token(SyntaxKind::NEWLINE.into(), "\n");
1900 nl_builder.finish_node();
1901 let nl_node = SyntaxNode::new_root_mut(nl_builder.finish());
1902 if let Some(token) = nl_node.first_token() {
1903 prev_entry.splice_children(
1904 entry_children_count..entry_children_count,
1905 vec![token.into()],
1906 );
1907 }
1908 }
1909 }
1910 }
1911
1912 let needs_indent = if pos > 0 {
1915 children
1916 .get(pos - 1)
1917 .and_then(|c| c.as_token())
1918 .map(|t| t.kind() == SyntaxKind::INDENT)
1919 .unwrap_or(false)
1920 } else {
1921 false
1922 };
1923
1924 if needs_indent {
1925 let indent_level = self.detect_indentation_level();
1926 if indent_level > 0 {
1927 let mut indent_builder = GreenNodeBuilder::new();
1928 indent_builder.start_node(SyntaxKind::ROOT.into());
1929 indent_builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent_level));
1930 indent_builder.finish_node();
1931 let indent_node = SyntaxNode::new_root_mut(indent_builder.finish());
1932 if let Some(token) = indent_node.first_token() {
1933 new_elements.push(token.into());
1934 }
1935 }
1936 }
1937
1938 let (entry, _has_trailing_newline) = self.create_mapping_entry(&new_key, &new_value);
1940
1941 new_elements.push(entry.into());
1943
1944 self.0.splice_children(pos..pos, new_elements);
1946 true
1947 } else {
1948 false
1949 }
1950 }
1951
1952 pub fn move_before(
1963 &self,
1964 before_key: impl crate::AsYaml,
1965 new_key: impl crate::AsYaml,
1966 new_value: impl crate::AsYaml,
1967 ) -> bool {
1968 self.move_before_impl(before_key, new_key, new_value)
1969 }
1970
1971 fn move_before_impl(
1973 &self,
1974 before_key: impl crate::AsYaml,
1975 new_key: impl crate::AsYaml,
1976 new_value: impl crate::AsYaml,
1977 ) -> bool {
1978 let children: Vec<_> = self.0.children_with_tokens().collect();
1979 let mut insert_position = None;
1980
1981 let mut i = 0;
1983 let mut removed_existing = false;
1984 while i < children.len() {
1985 if let Some(node) = children[i].as_node() {
1986 if node.kind() == SyntaxKind::MAPPING_ENTRY {
1987 for key_child in node.children() {
1989 if key_child.kind() == SyntaxKind::KEY
1990 && key_content_matches(&key_child, &new_key)
1991 {
1992 let mut remove_range = i..i + 1;
1994
1995 if i + 1 < children.len() {
1997 if let Some(token) = children[i + 1].as_token() {
1998 if token.kind() == SyntaxKind::NEWLINE {
1999 remove_range = i..i + 2;
2000 }
2001 }
2002 }
2003
2004 self.0.splice_children(remove_range, vec![]);
2005 removed_existing = true;
2006 break;
2007 }
2008 }
2009 if removed_existing {
2010 break;
2012 }
2013 } else if (node.kind() == SyntaxKind::KEY || node.kind() == SyntaxKind::SCALAR)
2014 && key_content_matches(node, &new_key)
2015 {
2016 for (offset, child_j) in children[(i + 1)..].iter().enumerate() {
2019 if let Some(node) = child_j.as_node() {
2020 if node.kind() == SyntaxKind::VALUE {
2021 let mut value_builder = GreenNodeBuilder::new();
2024 Document::build_value_content(&mut value_builder, &new_value, 2);
2025 let new_value_node =
2026 SyntaxNode::new_root_mut(value_builder.finish());
2027
2028 let j = i + 1 + offset;
2030 self.0
2031 .splice_children(j..j + 1, vec![new_value_node.into()]);
2032 return true;
2033 }
2034 }
2035 }
2036 return false;
2038 }
2039 }
2040 if !removed_existing {
2041 i += 1;
2042 }
2043 }
2044
2045 let children = if removed_existing {
2047 self.0.children_with_tokens().collect()
2048 } else {
2049 children
2050 };
2051
2052 for (i, child) in children.iter().enumerate() {
2054 if let Some(node) = child.as_node() {
2055 if node.kind() == SyntaxKind::MAPPING_ENTRY {
2056 for key_child in node.children() {
2058 if key_child.kind() == SyntaxKind::KEY
2059 && key_content_matches(&key_child, &before_key)
2060 {
2061 let mut line_start = i;
2063 for j in (0..i).rev() {
2064 if let Some(token) = children[j].as_token() {
2065 if token.kind() == SyntaxKind::NEWLINE {
2066 line_start = j + 1;
2067 break;
2068 }
2069 }
2070 }
2071 insert_position = Some(line_start);
2072 break;
2073 }
2074 }
2075 } else if (node.kind() == SyntaxKind::KEY || node.kind() == SyntaxKind::SCALAR)
2076 && key_content_matches(node, &before_key)
2077 {
2078 let mut line_start = i;
2081 for j in (0..i).rev() {
2082 if let Some(token) = children[j].as_token() {
2083 if token.kind() == SyntaxKind::NEWLINE {
2084 line_start = j + 1;
2085 break;
2086 }
2087 }
2088 }
2089 insert_position = Some(line_start);
2090 break;
2091 }
2092 }
2093 }
2094
2095 if let Some(pos) = insert_position {
2096 let mut new_elements = Vec::new();
2101
2102 let (entry, _has_trailing_newline) = self.create_mapping_entry(&new_key, &new_value);
2104 new_elements.push(entry.into());
2105
2106 self.0.splice_children(pos..pos, new_elements);
2111 true
2112 } else {
2113 false
2114 }
2115 }
2116
2117 pub fn insert_at_index_preserving(
2125 &self,
2126 index: usize,
2127 new_key: impl crate::AsYaml,
2128 new_value: impl crate::AsYaml,
2129 ) {
2130 let (new_entry, _has_trailing_newline) = self.create_mapping_entry(new_key, new_value);
2132
2133 if let Some(created_entry) = MappingEntry::cast(new_entry.clone()) {
2135 if let Some(created_key_node) = created_entry.key() {
2136 let existing_entry_opt = self.entries().find(|entry| {
2138 if let Some(entry_key) = entry.key() {
2139 self.compare_key_nodes(&entry_key, &created_key_node)
2140 } else {
2141 false
2142 }
2143 });
2144
2145 if let Some(existing_entry) = existing_entry_opt {
2146 let children: Vec<_> = self.0.children_with_tokens().collect();
2148 for (i, child) in children.iter().enumerate() {
2149 let Some(node) = child.as_node() else {
2150 continue;
2151 };
2152 if node != existing_entry.syntax() {
2153 continue;
2154 };
2155
2156 self.0.splice_children(i..i + 1, vec![new_entry.into()]);
2159
2160 return;
2161 }
2162 }
2163 }
2164 }
2165
2166 let children: Vec<_> = self.0.children_with_tokens().collect();
2168 let mut entry_indices = Vec::new();
2170 for (i, child) in children.iter().enumerate() {
2171 if let Some(node) = child.as_node() {
2172 if node.kind() == SyntaxKind::MAPPING_ENTRY {
2173 entry_indices.push(i);
2174 }
2175 }
2176 }
2177
2178 let mut new_elements = Vec::new();
2179
2180 let insert_pos = if entry_indices.is_empty() {
2182 0
2184 } else if index >= entry_indices.len() {
2185 let last_entry_idx = entry_indices[entry_indices.len() - 1];
2187 let mut pos = last_entry_idx + 1;
2189 while pos < children.len() {
2190 if let Some(token) = children[pos].as_token() {
2191 if token.kind() == SyntaxKind::NEWLINE {
2192 pos += 1;
2193 break;
2194 }
2195 }
2196 pos += 1;
2197 }
2198 pos
2199 } else {
2200 entry_indices[index]
2202 };
2203
2204 if insert_pos > 0 && !entry_indices.is_empty() {
2207 let has_newline_before = if let Some(child) = children.get(insert_pos - 1) {
2208 match child {
2209 rowan::NodeOrToken::Token(t) => t.kind() == SyntaxKind::NEWLINE,
2210 rowan::NodeOrToken::Node(n) => ends_with_newline(n),
2211 }
2212 } else {
2213 false
2214 };
2215
2216 if !has_newline_before {
2217 add_newline_token(&mut new_elements);
2218 }
2219 }
2220
2221 new_elements.push(new_entry.into());
2223
2224 self.0.splice_children(insert_pos..insert_pos, new_elements);
2226 }
2227
2228 pub fn remove(&self, key: impl crate::AsYaml) -> Option<MappingEntry> {
2237 let children: Vec<_> = self.0.children_with_tokens().collect();
2238
2239 for (i, child) in children.iter().enumerate() {
2241 if let Some(node) = child.as_node() {
2242 if node.kind() == SyntaxKind::MAPPING_ENTRY {
2243 if let Some(entry) = MappingEntry::cast(node.clone()) {
2244 if entry.key_matches(&key) {
2245 let is_last = !children.iter().skip(i + 1).any(|c| {
2247 c.as_node()
2248 .is_some_and(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
2249 });
2250
2251 self.0.splice_children(i..(i + 1), vec![]);
2253
2254 if is_last && i > 0 {
2255 if let Some(prev_entry_node) =
2258 children[..i].iter().rev().find_map(|c| {
2259 c.as_node()
2260 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
2261 })
2262 {
2263 if let Some(last_token) = prev_entry_node.last_token() {
2265 if last_token.kind() == SyntaxKind::NEWLINE {
2266 let entry_children_count =
2267 prev_entry_node.children_with_tokens().count();
2268 prev_entry_node.splice_children(
2269 (entry_children_count - 1)..entry_children_count,
2270 vec![],
2271 );
2272 }
2273 }
2274 }
2275 }
2276 return Some(entry);
2277 }
2278 }
2279 }
2280 }
2281 }
2282 None
2283 }
2284
2285 pub fn remove_nth_occurrence(&self, key: impl crate::AsYaml, n: usize) -> Option<MappingEntry> {
2321 let children: Vec<_> = self.0.children_with_tokens().collect();
2322
2323 let mut occurrence_count = 0;
2325 for (i, child) in children.iter().enumerate() {
2326 let node = child.as_node()?;
2327 if node.kind() != SyntaxKind::MAPPING_ENTRY {
2328 continue;
2329 }
2330
2331 let entry = MappingEntry::cast(node.clone())?;
2332 if !entry.key_matches(&key) {
2333 continue;
2334 }
2335
2336 if occurrence_count != n {
2337 occurrence_count += 1;
2338 continue;
2339 }
2340
2341 let is_last = !children.iter().skip(i + 1).any(|c| {
2343 c.as_node()
2344 .is_some_and(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
2345 });
2346
2347 self.0.splice_children(i..(i + 1), vec![]);
2348
2349 if is_last && i > 0 {
2350 let prev_entry_node = children[..i].iter().rev().find_map(|c| {
2352 c.as_node()
2353 .filter(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
2354 })?;
2355
2356 if let Some(last_token) = prev_entry_node.last_token() {
2357 if last_token.kind() == SyntaxKind::NEWLINE {
2358 let entry_children_count = prev_entry_node.children_with_tokens().count();
2359 prev_entry_node.splice_children(
2360 (entry_children_count - 1)..entry_children_count,
2361 vec![],
2362 );
2363 }
2364 }
2365 }
2366 return Some(entry);
2367 }
2368 None
2369 }
2370
2371 pub fn clear(&self) {
2375 let keys: Vec<crate::as_yaml::YamlNode> = self.keys().collect();
2376 for key in keys {
2377 self.remove(key);
2378 }
2379 }
2380
2381 pub fn rename_key(&self, old_key: impl crate::AsYaml, new_key: impl crate::AsYaml) -> bool {
2390 let children: Vec<_> = self.0.children_with_tokens().collect();
2391
2392 for (i, child) in children.iter().enumerate() {
2393 if let Some(node) = child.as_node() {
2394 if node.kind() == SyntaxKind::MAPPING_ENTRY {
2395 if let Some(key_node) = node.children().find(|n| n.kind() == SyntaxKind::KEY) {
2396 if key_content_matches(&key_node, &old_key) {
2397 let entry_children: Vec<_> = node.children_with_tokens().collect();
2398
2399 let mut builder = GreenNodeBuilder::new();
2400 builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
2401
2402 for entry_child in entry_children {
2403 match entry_child {
2404 rowan::NodeOrToken::Node(n) if n.kind() == SyntaxKind::KEY => {
2405 builder.start_node(SyntaxKind::KEY.into());
2407 new_key.build_content(&mut builder, 0, false);
2408 builder.finish_node(); }
2410 rowan::NodeOrToken::Node(n) => {
2411 crate::yaml::copy_node_to_builder(&mut builder, &n);
2412 }
2413 rowan::NodeOrToken::Token(t) => {
2414 builder.token(t.kind().into(), t.text());
2415 }
2416 }
2417 }
2418
2419 builder.finish_node();
2420 let new_entry = SyntaxNode::new_root_mut(builder.finish());
2421 self.0.splice_children(i..i + 1, vec![new_entry.into()]);
2422 return true;
2423 }
2424 }
2425 }
2426 }
2427 }
2428 false
2429 }
2430
2431 fn create_mapping_entry(
2433 &self,
2434 key: impl crate::AsYaml,
2435 value: impl crate::AsYaml,
2436 ) -> (SyntaxNode, bool) {
2437 let mut builder = GreenNodeBuilder::new();
2438 builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
2439
2440 builder.start_node(SyntaxKind::KEY.into());
2442 key.build_content(&mut builder, 0, false);
2443 builder.finish_node(); builder.token(SyntaxKind::COLON.into(), ":");
2447
2448 if !value.is_inline() {
2450 builder.token(SyntaxKind::NEWLINE.into(), "\n");
2451 builder.token(SyntaxKind::INDENT.into(), " "); } else {
2453 builder.token(SyntaxKind::WHITESPACE.into(), " ");
2454 }
2455
2456 builder.start_node(SyntaxKind::VALUE.into());
2458 let value_ends_with_newline = value.build_content(&mut builder, 2, false);
2459 builder.finish_node(); let added_newline = if !value_ends_with_newline {
2464 builder.token(SyntaxKind::NEWLINE.into(), "\n");
2465 true
2466 } else {
2467 false
2468 };
2469
2470 builder.finish_node(); (
2472 SyntaxNode::new_root_mut(builder.finish()),
2473 added_newline || value_ends_with_newline,
2474 )
2475 }
2476
2477 pub fn insert_after(
2489 &self,
2490 after_key: impl crate::AsYaml,
2491 key: impl crate::AsYaml,
2492 value: impl crate::AsYaml,
2493 ) -> bool {
2494 if self.find_entry_by_key(&key).is_some() {
2496 self.set_as_yaml(&key, &value);
2497 return true;
2498 }
2499
2500 self.move_after(after_key, key, value)
2505 }
2506
2507 pub fn insert_before(
2519 &self,
2520 before_key: impl crate::AsYaml,
2521 key: impl crate::AsYaml,
2522 value: impl crate::AsYaml,
2523 ) -> bool {
2524 if self.find_entry_by_key(&key).is_some() {
2526 self.set_as_yaml(&key, &value);
2527 return self.find_entry_by_key(&before_key).is_some();
2530 }
2531
2532 self.move_before(before_key, key, value)
2536 }
2537
2538 pub fn insert_at_index(
2547 &self,
2548 index: usize,
2549 key: impl crate::AsYaml,
2550 value: impl crate::AsYaml,
2551 ) {
2552 if self.find_entry_by_key(&key).is_some() {
2554 self.set_as_yaml(&key, &value);
2555 return;
2556 }
2557
2558 let flow_context = self.is_flow_style();
2560 let use_explicit_keys = self.uses_explicit_keys();
2561 let key_indent = self.detect_indentation_level();
2562 let new_entry =
2563 MappingEntry::new_at_indent(&key, &value, flow_context, use_explicit_keys, key_indent);
2564
2565 let entry_count = self.entries().count();
2567 let actual_index = index.min(entry_count);
2568
2569 let mut entry_positions = Vec::new();
2571 for (i, child) in self.0.children_with_tokens().enumerate() {
2572 if child
2573 .as_node()
2574 .is_some_and(|n| n.kind() == SyntaxKind::MAPPING_ENTRY)
2575 {
2576 entry_positions.push(i);
2577 }
2578 }
2579
2580 let insert_pos = if actual_index < entry_positions.len() {
2582 entry_positions[actual_index]
2583 } else {
2584 self.0.children_with_tokens().count()
2585 };
2586
2587 let mut new_elements = Vec::new();
2589
2590 if insert_pos > 0 {
2592 let needs_newline =
2596 if let Some(prev) = self.0.children_with_tokens().nth(insert_pos - 1) {
2597 match prev {
2598 rowan::NodeOrToken::Token(t) => t.kind() != SyntaxKind::NEWLINE,
2599 rowan::NodeOrToken::Node(n) => !ends_with_newline(&n),
2600 }
2601 } else {
2602 false
2603 };
2604
2605 if needs_newline {
2606 add_newline_token(&mut new_elements);
2607 }
2608
2609 let indent_level = self.detect_indentation_level();
2611 if indent_level > 0 {
2612 let mut indent_builder = GreenNodeBuilder::new();
2613 indent_builder.start_node(SyntaxKind::ROOT.into());
2614 indent_builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent_level));
2615 indent_builder.finish_node();
2616 let indent_node = SyntaxNode::new_root_mut(indent_builder.finish());
2617 if let Some(token) = indent_node.first_token() {
2618 new_elements.push(token.into());
2619 }
2620 }
2621 }
2622
2623 new_elements.push(new_entry.0.into());
2625
2626 self.0.splice_children(insert_pos..insert_pos, new_elements);
2628 }
2629
2630 pub fn byte_range(&self) -> crate::TextPosition {
2634 self.0.text_range().into()
2635 }
2636
2637 pub fn start_position(&self, source_text: &str) -> crate::LineColumn {
2646 let range = self.byte_range();
2647 crate::byte_offset_to_line_column(source_text, range.start as usize)
2648 }
2649
2650 pub fn end_position(&self, source_text: &str) -> crate::LineColumn {
2659 let range = self.byte_range();
2660 crate::byte_offset_to_line_column(source_text, range.end as usize)
2661 }
2662}
2663
2664impl Default for Mapping {
2665 fn default() -> Self {
2666 Self::new()
2667 }
2668}
2669
2670impl<'a> IntoIterator for &'a Mapping {
2673 type Item = (crate::as_yaml::YamlNode, crate::as_yaml::YamlNode);
2674 type IntoIter =
2675 Box<dyn Iterator<Item = (crate::as_yaml::YamlNode, crate::as_yaml::YamlNode)> + 'a>;
2676
2677 fn into_iter(self) -> Self::IntoIter {
2678 Box::new(self.iter())
2679 }
2680}
2681
2682impl AsYaml for Mapping {
2683 fn as_node(&self) -> Option<&SyntaxNode> {
2684 Some(&self.0)
2685 }
2686
2687 fn kind(&self) -> YamlKind {
2688 YamlKind::Mapping
2689 }
2690
2691 fn build_content(
2692 &self,
2693 builder: &mut rowan::GreenNodeBuilder,
2694 indent: usize,
2695 _flow_context: bool,
2696 ) -> bool {
2697 builder.start_node(SyntaxKind::MAPPING.into());
2698 crate::as_yaml::copy_node_content_with_indent(builder, &self.0, indent);
2699 builder.finish_node();
2700 self.0
2701 .last_token()
2702 .map(|t| t.kind() == SyntaxKind::NEWLINE)
2703 .unwrap_or(false)
2704 }
2705
2706 fn is_inline(&self) -> bool {
2707 ValueNode::is_inline(self)
2708 }
2709}
2710#[cfg(test)]
2711mod tests {
2712 use crate::scalar::ScalarValue;
2713 use crate::yaml::YamlFile;
2714 use std::str::FromStr;
2715
2716 #[test]
2717 fn test_mapping_set_new_key() {
2718 let yaml = "existing: value";
2719 let parsed = YamlFile::from_str(yaml).unwrap();
2720
2721 let doc = parsed.document().expect("Should have a document");
2723 doc.set("new_key", "new_value");
2724
2725 let output = doc.to_string();
2726
2727 let expected = r#"existing: value
2728new_key: new_value"#;
2729 assert_eq!(output.trim(), expected);
2730 }
2731 #[test]
2732 fn test_mapping_rename_key() {
2733 let yaml = "old_name: value";
2734 let parsed = YamlFile::from_str(yaml).unwrap();
2735
2736 let doc = parsed.document().expect("expected a document");
2737 let mapping = doc.as_mapping().expect("expected a mapping");
2738 let renamed = mapping.rename_key("old_name", "new_name");
2739 assert!(renamed);
2740 assert!(doc.contains_key("new_name"));
2741 assert!(!doc.contains_key("old_name"));
2742 }
2743
2744 #[test]
2745 fn test_mapping_remove_key() {
2746 let yaml = "key1: value1\nkey2: value2";
2747 let parsed = YamlFile::from_str(yaml).unwrap();
2748
2749 let doc = parsed.document().expect("expected a document");
2750 let mapping = doc.as_mapping().expect("expected a mapping");
2751 let removed = mapping.remove("key1");
2752 assert!(removed.is_some());
2753 assert!(!doc.contains_key("key1"));
2754 assert!(doc.contains_key("key2"));
2755 }
2756 #[test]
2757 fn test_mapping_simple_set() {
2758 let yaml = "key1: value1";
2759 let parsed = YamlFile::from_str(yaml).unwrap();
2760
2761 let doc = parsed.document().expect("Should have a document");
2763 doc.set("key2", "value2");
2764
2765 let output = doc.to_string();
2766
2767 let expected = r#"key1: value1
2768key2: value2"#;
2769 assert_eq!(output.trim(), expected);
2770 }
2771 #[test]
2772 fn test_mapping_set_preserves_position() {
2773 let yaml = r#"Name: original_name
2775Contact: original_contact
2776Repository: https://github.com/example/repo.git
2777"#;
2778 let parsed = YamlFile::from_str(yaml).unwrap();
2779 let doc = parsed.document().expect("Should have a document");
2780
2781 doc.set("Contact", "updated_contact");
2783
2784 let output = doc.to_string();
2785 let expected = r#"Name: original_name
2786Contact: updated_contact
2787Repository: https://github.com/example/repo.git
2788"#;
2789 assert_eq!(output, expected);
2790 }
2791 #[test]
2792 fn test_mapping_set_preserves_multiple_fields() {
2793 let yaml = r#"Name: tsne
2795Contact: Justin Donaldson <jdonaldson@gmail.com>
2796Archive: CRAN
2797Repository: https://github.com/jdonaldson/rtsne.git
2798"#;
2799 let parsed = YamlFile::from_str(yaml).unwrap();
2800 let doc = parsed.document().expect("Should have a document");
2801
2802 if let Some(mapping) = doc.as_mapping() {
2803 mapping.set("Contact", "New Contact <new@example.com>");
2805 mapping.set("Archive", "PyPI");
2807 }
2808
2809 let output = doc.to_string();
2810 let expected = r#"Name: tsne
2811Contact: New Contact <new@example.com>
2812Archive: PyPI
2813Repository: https://github.com/jdonaldson/rtsne.git
2814"#;
2815 assert_eq!(output, expected);
2816 }
2817 #[test]
2818 fn test_mapping_insert_after() {
2819 let yaml = r#"first: 1
2820second: 2
2821fourth: 4"#;
2822
2823 let parsed = YamlFile::from_str(yaml).unwrap();
2824
2825 let doc = parsed.document().expect("Should have a document");
2826
2827 let success = doc.insert_after("second", "third", 3);
2829 assert!(
2830 success,
2831 "insert_after should succeed when reference key exists"
2832 );
2833
2834 let output = doc.to_string();
2835
2836 let expected = r#"first: 1
2838second: 2
2839third: 3
2840fourth: 4"#;
2841 assert_eq!(output.trim(), expected);
2842
2843 let failed = doc.insert_after("nonexistent", "new_key", "new_value");
2845 assert!(
2846 !failed,
2847 "insert_after should fail when reference key doesn't exist"
2848 );
2849
2850 let updated = doc.insert_after("first", "second", "2_updated");
2852 assert!(updated, "insert_after should update existing key");
2853 let updated_output = doc.to_string();
2854 let expected_updated = r#"first: 1
2855second: 2_updated
2856third: 3
2857fourth: 4"#;
2858 assert_eq!(updated_output.trim(), expected_updated);
2859 }
2860 #[test]
2861 fn test_mapping_insert_before() {
2862 let yaml = r#"first: 1
2863third: 3
2864fourth: 4"#;
2865
2866 let parsed = YamlFile::from_str(yaml).unwrap();
2867 let doc = parsed.document().expect("Should have a document");
2868
2869 let success = doc.insert_before("third", "second", 2);
2871 assert!(
2872 success,
2873 "insert_before should succeed when reference key exists"
2874 );
2875
2876 let output = doc.to_string();
2877
2878 let expected = r#"first: 1
2880second: 2
2881third: 3
2882fourth: 4"#;
2883 assert_eq!(output.trim(), expected);
2884
2885 let failed = doc.insert_before("nonexistent", "new_key", "new_value");
2887 assert!(
2888 !failed,
2889 "insert_before should fail when reference key doesn't exist"
2890 );
2891
2892 let updated = doc.insert_before("fourth", "third", "3_updated");
2894 assert!(updated, "insert_before should update existing key");
2895 let output = doc.to_string();
2896 let expected_updated = r#"first: 1
2897second: 2
2898third: 3_updated
2899fourth: 4"#;
2900 assert_eq!(output.trim(), expected_updated);
2901 }
2902 #[test]
2903 fn test_mapping_insert_at_index() {
2904 let yaml = r#"first: 1
2905third: 3"#;
2906
2907 let parsed = YamlFile::from_str(yaml).unwrap();
2908 let doc = parsed.document().expect("Should have a document");
2909
2910 doc.insert_at_index(1, "second", 2);
2912
2913 let output = doc.to_string();
2914
2915 let expected = r#"first: 1
2917second: 2
2918third: 3"#;
2919 assert_eq!(output.trim(), expected);
2920
2921 doc.insert_at_index(0, "zero", 0);
2923 let output2 = doc.to_string();
2924 let expected2 = r#"zero: 0
2925first: 1
2926second: 2
2927third: 3"#;
2928 assert_eq!(output2.trim(), expected2);
2929
2930 doc.insert_at_index(100, "last", "999");
2932 let output3 = doc.to_string();
2933 let expected3 = r#"zero: 0
2934first: 1
2935second: 2
2936third: 3
2937last: '999'"#;
2938 assert_eq!(output3.trim(), expected3);
2939
2940 doc.insert_at_index(2, "first", "1_updated");
2942 let final_output = doc.to_string();
2943 let expected_final = r#"zero: 0
2944first: 1_updated
2945second: 2
2946third: 3
2947last: '999'"#;
2948 assert_eq!(final_output.trim(), expected_final);
2949 }
2950 #[test]
2951 fn test_mapping_insert_special_characters() {
2952 let yaml = "key1: value1";
2953
2954 let parsed = YamlFile::from_str(yaml).unwrap();
2955 let doc = parsed.document().expect("Should have a document");
2956
2957 doc.insert_after("key1", "special:key", "value:with:colons");
2959 doc.insert_before("key1", "key with spaces", "value with spaces");
2960 doc.insert_at_index(1, "key@symbol", "value#hash");
2961
2962 assert!(doc.contains_key("special:key"));
2964 assert!(doc.contains_key("key with spaces"));
2965 assert!(doc.contains_key("key@symbol"));
2966
2967 let output = doc.to_string();
2969 let reparsed = YamlFile::from_str(&output);
2970 assert!(reparsed.is_ok(), "Output should be valid YAML");
2971 }
2972 #[test]
2973 fn test_mapping_insert_empty_values() {
2974 let yaml = "key1: value1";
2975
2976 let parsed = YamlFile::from_str(yaml).unwrap();
2977 let doc = parsed.document().expect("Should have a document");
2978
2979 doc.insert_after("key1", "empty", "");
2981 doc.insert_before("key1", "null_key", ScalarValue::null());
2982
2983 assert!(doc.contains_key("empty"));
2984 assert!(doc.contains_key("null_key"));
2985
2986 let output = parsed.to_string();
2988 let reparsed = YamlFile::from_str(&output);
2989 assert!(
2990 reparsed.is_ok(),
2991 "Output with empty values should be valid YAML"
2992 );
2993 }
2994
2995 #[test]
2998 fn test_mapping_into_iterator() {
2999 use crate::Document;
3000 let text = "name: Alice\nage: 30\ncity: Boston";
3001 let doc = Document::from_str(text).unwrap();
3002 let mapping = doc.as_mapping().unwrap();
3003
3004 let mut count = 0;
3006 for (key, value) in &mapping {
3007 count += 1;
3008
3009 assert!(key.is_scalar());
3011 assert!(value.is_scalar());
3012 }
3013
3014 assert_eq!(count, 3);
3015 }
3016
3017 #[test]
3018 fn test_mapping_into_iterator_collect() {
3019 use crate::Document;
3020 let text = "a: 1\nb: 2\nc: 3";
3021 let doc = Document::from_str(text).unwrap();
3022 let mapping = doc.as_mapping().unwrap();
3023
3024 let pairs: Vec<_> = (&mapping).into_iter().collect();
3026 assert_eq!(pairs.len(), 3);
3027
3028 for (key, value) in pairs {
3030 assert!(key.as_scalar().is_some());
3031 assert!(value.as_scalar().is_some());
3032 }
3033 }
3034
3035 #[test]
3036 fn test_mapping_iterator_filter() {
3037 use crate::Document;
3038 let text = "a: 1\nb: 2\nc: 3\nd: 4";
3039 let doc = Document::from_str(text).unwrap();
3040 let mapping = doc.as_mapping().unwrap();
3041
3042 let even_count = (&mapping)
3044 .into_iter()
3045 .filter(|(_, value)| {
3046 value
3047 .as_scalar()
3048 .and_then(|s| s.to_string().parse::<i32>().ok())
3049 .map(|n| n % 2 == 0)
3050 .unwrap_or(false)
3051 })
3052 .count();
3053
3054 assert_eq!(even_count, 2); }
3056
3057 #[test]
3058 fn test_empty_mapping_iterator() {
3059 let empty = crate::Mapping::new();
3060
3061 let count = (&empty).into_iter().count();
3062 assert_eq!(count, 0);
3063 }
3064
3065 #[test]
3066 fn test_nested_mapping_iteration() {
3067 use crate::Document;
3068 let text = "server:\n host: localhost\n port: 8080";
3069 let doc = Document::from_str(text).unwrap();
3070 let mapping = doc.as_mapping().unwrap();
3071
3072 for (key, _value) in &mapping {
3074 if let Some(key_scalar) = key.as_scalar() {
3075 if key_scalar.to_string() == "server" {
3076 if let Some(nested_mapping) = mapping.get_mapping("server") {
3078 let nested_count = (&nested_mapping).into_iter().count();
3079 assert_eq!(nested_count, 2); }
3081 }
3082 }
3083 }
3084 }
3085
3086 #[test]
3091 fn test_mapping_keys() {
3092 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3093 let doc = yaml.document().unwrap();
3094 let mapping = doc.as_mapping().unwrap();
3095
3096 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3097 assert_eq!(keys, vec!["name", "age", "city"]);
3098 }
3099
3100 #[test]
3101 fn test_mapping_is_empty() {
3102 let yaml = YamlFile::from_str("{}").unwrap();
3103 let doc = yaml.document().unwrap();
3104 let mapping = doc.as_mapping().unwrap();
3105 assert!(mapping.is_empty());
3106
3107 let yaml2 = YamlFile::from_str("key: value").unwrap();
3108 let doc2 = yaml2.document().unwrap();
3109 let mapping2 = doc2.as_mapping().unwrap();
3110 assert!(!mapping2.is_empty());
3111 }
3112
3113 #[test]
3114 fn test_mapping_contains_key() {
3115 let yaml = YamlFile::from_str("name: Alice\nage: 30").unwrap();
3116 let doc = yaml.document().unwrap();
3117 let mapping = doc.as_mapping().unwrap();
3118
3119 assert!(mapping.contains_key("name"));
3120 assert!(mapping.contains_key("age"));
3121 assert!(!mapping.contains_key("city"));
3122 }
3123
3124 #[test]
3125 fn test_mapping_get() {
3126 let yaml = YamlFile::from_str("name: Alice\nage: 30").unwrap();
3127 let doc = yaml.document().unwrap();
3128 let mapping = doc.as_mapping().unwrap();
3129
3130 assert_eq!(
3131 mapping
3132 .get("name")
3133 .and_then(|v| v.as_scalar().map(|s| s.as_string())),
3134 Some("Alice".to_string())
3135 );
3136 assert_eq!(mapping.get("age").and_then(|v| v.to_i64()), Some(30));
3137 assert!(mapping.get("city").is_none());
3138 }
3139
3140 #[test]
3141 fn test_mapping_single_entry() {
3142 let yaml = YamlFile::from_str("key: value").unwrap();
3143 let doc = yaml.document().unwrap();
3144 let mapping = doc.as_mapping().unwrap();
3145
3146 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3147 assert_eq!(keys, vec!["key"]);
3148 assert!(!mapping.is_empty());
3149 assert!(mapping.contains_key("key"));
3150 }
3151
3152 #[test]
3155 fn test_mapping_ops_set_new_key() {
3156 let yaml = YamlFile::from_str("name: Alice").unwrap();
3157 let doc = yaml.document().unwrap();
3158 let mapping = doc.as_mapping().unwrap();
3159
3160 mapping.set("age", 30);
3161
3162 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3163 assert_eq!(keys, vec!["name", "age"]);
3164 assert_eq!(mapping.get("age").and_then(|v| v.to_i64()), Some(30));
3165 }
3166
3167 #[test]
3168 fn test_mapping_set_existing_key() {
3169 let yaml = YamlFile::from_str("name: Alice\nage: 30").unwrap();
3170 let doc = yaml.document().unwrap();
3171 let mapping = doc.as_mapping().unwrap();
3172
3173 mapping.set("age", 31);
3174
3175 assert_eq!(mapping.get("age").and_then(|v| v.to_i64()), Some(31));
3176 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3177 assert_eq!(keys, vec!["name", "age"]);
3178 }
3179
3180 #[test]
3181 fn test_mapping_remove_existing_key() {
3182 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3183 let doc = yaml.document().unwrap();
3184 let mapping = doc.as_mapping().unwrap();
3185
3186 let removed = mapping.remove("age");
3187 assert!(removed.is_some());
3188
3189 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3190 assert_eq!(keys, vec!["name", "city"]);
3191 assert!(!mapping.contains_key("age"));
3192 }
3193
3194 #[test]
3195 fn test_mapping_remove_nonexistent_key() {
3196 let yaml = YamlFile::from_str("name: Alice").unwrap();
3197 let doc = yaml.document().unwrap();
3198 let mapping = doc.as_mapping().unwrap();
3199
3200 let removed = mapping.remove("age");
3201 assert!(removed.is_none());
3202
3203 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3204 assert_eq!(keys, vec!["name"]);
3205 }
3206
3207 #[test]
3208 fn test_mapping_remove_all_keys() {
3209 let yaml = YamlFile::from_str("a: 1\nb: 2").unwrap();
3210 let doc = yaml.document().unwrap();
3211 let mapping = doc.as_mapping().unwrap();
3212
3213 assert!(mapping.remove("a").is_some());
3214 assert!(mapping.remove("b").is_some());
3215 assert!(mapping.is_empty());
3216 }
3217
3218 #[test]
3219 fn test_rename_key_basic() {
3220 let original = r#"name: my-app
3221version: 1.0
3222author: Alice"#;
3223
3224 let yaml = YamlFile::from_str(original).unwrap();
3225
3226 if let Some(doc) = yaml.document() {
3227 if let Some(mapping) = doc.as_mapping() {
3228 let success = mapping.rename_key("version", "app_version");
3229 assert!(success);
3230 }
3231 }
3232
3233 let expected = r#"name: my-app
3234app_version: 1.0
3235author: Alice"#;
3236 assert_eq!(yaml.to_string(), expected);
3237 }
3238
3239 #[test]
3240 fn test_rename_key_preserves_value() {
3241 let original = r#"count: 42
3242enabled: true"#;
3243
3244 let yaml = YamlFile::from_str(original).unwrap();
3245
3246 if let Some(doc) = yaml.document() {
3247 if let Some(mapping) = doc.as_mapping() {
3248 mapping.rename_key("count", "total");
3249 }
3250 }
3251
3252 let expected = r#"total: 42
3253enabled: true"#;
3254 assert_eq!(yaml.to_string(), expected);
3255 }
3256
3257 #[test]
3258 fn test_remove_field() {
3259 let original = r#"name: my-app
3260version: 1.0
3261author: Alice"#;
3262
3263 let yaml = YamlFile::from_str(original).unwrap();
3264
3265 if let Some(doc) = yaml.document() {
3266 if let Some(mapping) = doc.as_mapping() {
3267 let removed = mapping.remove("author");
3268 assert!(removed.is_some());
3269 }
3270 }
3271
3272 let expected = r#"name: my-app
3273version: 1.0"#;
3274 assert_eq!(yaml.to_string(), expected);
3275 }
3276
3277 #[test]
3278 fn test_complex_operations_combined() {
3279 let original = r#"name: my-app
3280version: 1.0
3281author: Alice
3282year: 2023
3283
3284features:
3285 - logging
3286 - auth"#;
3287
3288 let yaml = YamlFile::from_str(original).unwrap();
3289
3290 if let Some(doc) = yaml.document() {
3291 if let Some(mapping) = doc.as_mapping() {
3292 mapping.set("license", "MIT");
3294 mapping.set("published", true);
3295 mapping.set("downloads", 1000);
3296
3297 mapping.remove("author");
3299
3300 mapping.rename_key("version", "app_version");
3302
3303 mapping.set("year", 2024);
3305 }
3306 }
3307
3308 let expected = r#"name: my-app
3309app_version: 1.0
3310year: 2024
3311
3312features:
3313 - logging
3314 - auth
3315license: MIT
3316published: true
3317downloads: 1000
3318"#;
3319 assert_eq!(yaml.to_string(), expected);
3320 }
3321
3322 #[test]
3325 fn test_mapping_get_nested_mapping() {
3326 let yaml = YamlFile::from_str("user:\n name: Alice\n age: 30").unwrap();
3327 let doc = yaml.document().unwrap();
3328 let mapping = doc.as_mapping().unwrap();
3329
3330 let nested = mapping.get_mapping("user");
3331 assert!(nested.is_some());
3332
3333 let nested = nested.unwrap();
3334 assert_eq!(
3335 nested
3336 .get("name")
3337 .and_then(|v| v.as_scalar().map(|s| s.as_string())),
3338 Some("Alice".to_string())
3339 );
3340 assert_eq!(nested.get("age").and_then(|v| v.to_i64()), Some(30));
3341 }
3342
3343 #[test]
3344 fn test_mapping_get_nested_sequence() {
3345 let yaml = YamlFile::from_str("items:\n - a\n - b\n - c").unwrap();
3346 let doc = yaml.document().unwrap();
3347 let mapping = doc.as_mapping().unwrap();
3348
3349 let seq = mapping.get_sequence("items");
3350 assert!(seq.is_some());
3351
3352 let seq = seq.unwrap();
3353 assert_eq!(seq.len(), 3);
3354 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
3355 assert_eq!(values, vec!["a", "b", "c"]);
3356 }
3357
3358 #[test]
3359 fn test_mapping_get_nonexistent_nested() {
3360 let yaml = YamlFile::from_str("name: Alice").unwrap();
3361 let doc = yaml.document().unwrap();
3362 let mapping = doc.as_mapping().unwrap();
3363
3364 assert_eq!(mapping.get_mapping("user"), None);
3365 assert_eq!(mapping.get_sequence("items"), None);
3366 }
3367
3368 #[test]
3371 fn test_rename_key_nonexistent() {
3372 let yaml = YamlFile::from_str("name: Alice").unwrap();
3373 let doc = yaml.document().unwrap();
3374 let mapping = doc.as_mapping().unwrap();
3375
3376 let success = mapping.rename_key("age", "years");
3377 assert!(!success);
3378
3379 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3380 assert_eq!(keys, vec!["name"]);
3381 }
3382
3383 #[test]
3384 fn test_rename_key_first_entry() {
3385 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3386 let doc = yaml.document().unwrap();
3387 let mapping = doc.as_mapping().unwrap();
3388
3389 let success = mapping.rename_key("name", "username");
3390 assert!(success);
3391
3392 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3393 assert_eq!(keys, vec!["username", "age", "city"]);
3394 }
3395
3396 #[test]
3397 fn test_rename_key_middle_entry() {
3398 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3399 let doc = yaml.document().unwrap();
3400 let mapping = doc.as_mapping().unwrap();
3401
3402 let success = mapping.rename_key("age", "years");
3403 assert!(success);
3404
3405 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3406 assert_eq!(keys, vec!["name", "years", "city"]);
3407 }
3408
3409 #[test]
3410 fn test_rename_key_last_entry() {
3411 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3412 let doc = yaml.document().unwrap();
3413 let mapping = doc.as_mapping().unwrap();
3414
3415 let success = mapping.rename_key("city", "location");
3416 assert!(success);
3417
3418 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3419 assert_eq!(keys, vec!["name", "age", "location"]);
3420 }
3421
3422 #[test]
3425 fn test_mapping_with_different_value_types() {
3426 let yaml = YamlFile::from_str("string: hello\nnumber: 42\nbool: true").unwrap();
3427 let doc = yaml.document().unwrap();
3428 let mapping = doc.as_mapping().unwrap();
3429
3430 assert_eq!(
3431 mapping
3432 .get("string")
3433 .and_then(|v| v.as_scalar().map(|s| s.as_string())),
3434 Some("hello".to_string())
3435 );
3436 assert_eq!(mapping.get("number").and_then(|v| v.to_i64()), Some(42));
3437 assert_eq!(mapping.get("bool").and_then(|v| v.to_bool()), Some(true));
3438 }
3439
3440 #[test]
3441 fn test_mapping_set_different_value_types() {
3442 let yaml = YamlFile::from_str("key: value").unwrap();
3443 let doc = yaml.document().unwrap();
3444 let mapping = doc.as_mapping().unwrap();
3445
3446 mapping.set("number", 123);
3447 mapping.set("bool", false);
3448 mapping.set("text", "hello");
3449
3450 assert_eq!(mapping.get("number").and_then(|v| v.to_i64()), Some(123));
3451 assert_eq!(mapping.get("bool").and_then(|v| v.to_bool()), Some(false));
3452 assert_eq!(
3453 mapping
3454 .get("text")
3455 .and_then(|v| v.as_scalar().map(|s| s.as_string())),
3456 Some("hello".to_string())
3457 );
3458 }
3459
3460 #[test]
3463 fn test_empty_mapping_operations() {
3464 let yaml = YamlFile::from_str("{}").unwrap();
3465 let doc = yaml.document().unwrap();
3466 let mapping = doc.as_mapping().unwrap();
3467
3468 assert!(mapping.is_empty());
3469 assert!(!mapping.contains_key("any"));
3470 assert_eq!(mapping.get("any"), None);
3471 assert!(mapping.remove("any").is_none());
3472 assert!(!mapping.rename_key("old", "new"));
3473
3474 mapping.set("first", "value");
3476 assert!(!mapping.is_empty());
3477 assert_eq!(
3479 mapping.get("first").map(|v| v.to_string()),
3480 Some("\"value\"".to_string())
3481 );
3482 }
3483
3484 #[test]
3485 fn test_mapping_remove_first_of_three() {
3486 let yaml = YamlFile::from_str("a: 1\nb: 2\nc: 3").unwrap();
3487 let doc = yaml.document().unwrap();
3488 let mapping = doc.as_mapping().unwrap();
3489
3490 assert!(mapping.remove("a").is_some());
3491
3492 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3493 assert_eq!(keys, vec!["b", "c"]);
3494 }
3495
3496 #[test]
3497 fn test_mapping_remove_middle_of_three() {
3498 let yaml = YamlFile::from_str("a: 1\nb: 2\nc: 3").unwrap();
3499 let doc = yaml.document().unwrap();
3500 let mapping = doc.as_mapping().unwrap();
3501
3502 assert!(mapping.remove("b").is_some());
3503
3504 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3505 assert_eq!(keys, vec!["a", "c"]);
3506 }
3507
3508 #[test]
3509 fn test_mapping_remove_last_of_three() {
3510 let yaml = YamlFile::from_str("a: 1\nb: 2\nc: 3").unwrap();
3511 let doc = yaml.document().unwrap();
3512 let mapping = doc.as_mapping().unwrap();
3513
3514 assert!(mapping.remove("c").is_some());
3515
3516 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3517 assert_eq!(keys, vec!["a", "b"]);
3518 }
3519
3520 #[test]
3523 fn test_mapping_len_empty() {
3524 let yaml = YamlFile::from_str("{}").unwrap();
3525 let doc = yaml.document().unwrap();
3526 let mapping = doc.as_mapping().unwrap();
3527
3528 assert_eq!(mapping.len(), 0);
3529 assert!(mapping.is_empty());
3530 }
3531
3532 #[test]
3533 fn test_mapping_len_single() {
3534 let yaml = YamlFile::from_str("name: Alice").unwrap();
3535 let doc = yaml.document().unwrap();
3536 let mapping = doc.as_mapping().unwrap();
3537
3538 assert_eq!(mapping.len(), 1);
3539 assert!(!mapping.is_empty());
3540 }
3541
3542 #[test]
3543 fn test_mapping_len_multiple() {
3544 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3545 let doc = yaml.document().unwrap();
3546 let mapping = doc.as_mapping().unwrap();
3547
3548 assert_eq!(mapping.len(), 3);
3549 assert!(!mapping.is_empty());
3550 }
3551
3552 #[test]
3553 fn test_mapping_len_after_adding() {
3554 let yaml = YamlFile::from_str("name: Alice").unwrap();
3555 let doc = yaml.document().unwrap();
3556 let mapping = doc.as_mapping().unwrap();
3557
3558 assert_eq!(mapping.len(), 1);
3559
3560 mapping.set("age", 30);
3561 assert_eq!(mapping.len(), 2);
3562
3563 mapping.set("city", "NYC");
3564 assert_eq!(mapping.len(), 3);
3565 }
3566
3567 #[test]
3568 fn test_mapping_len_after_removing() {
3569 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3570 let doc = yaml.document().unwrap();
3571 let mapping = doc.as_mapping().unwrap();
3572
3573 assert_eq!(mapping.len(), 3);
3574
3575 mapping.remove("age");
3576 assert_eq!(mapping.len(), 2);
3577
3578 mapping.remove("city");
3579 assert_eq!(mapping.len(), 1);
3580
3581 mapping.remove("name");
3582 assert_eq!(mapping.len(), 0);
3583 assert!(mapping.is_empty());
3584 }
3585
3586 #[test]
3587 fn test_mapping_values_empty() {
3588 let yaml = YamlFile::from_str("{}").unwrap();
3589 let doc = yaml.document().unwrap();
3590 let mapping = doc.as_mapping().unwrap();
3591
3592 let values: Vec<_> = mapping.values().collect();
3593 assert_eq!(values.len(), 0);
3594 }
3595
3596 #[test]
3597 fn test_mapping_values_single() {
3598 let yaml = YamlFile::from_str("name: Alice").unwrap();
3599 let doc = yaml.document().unwrap();
3600 let mapping = doc.as_mapping().unwrap();
3601
3602 let values: Vec<_> = mapping.values().collect();
3603 assert_eq!(values.len(), 1);
3604 assert_eq!(
3605 values[0].as_scalar().map(|s| s.as_string()),
3606 Some("Alice".to_string())
3607 );
3608 }
3609
3610 #[test]
3611 fn test_mapping_values_multiple() {
3612 let yaml = YamlFile::from_str("name: Alice\nage: 30\nactive: true").unwrap();
3613 let doc = yaml.document().unwrap();
3614 let mapping = doc.as_mapping().unwrap();
3615
3616 let values: Vec<_> = mapping.values().collect();
3617 assert_eq!(values.len(), 3);
3618 assert_eq!(
3619 values[0].as_scalar().map(|s| s.as_string()),
3620 Some("Alice".to_string())
3621 );
3622 assert_eq!(values[1].to_i64(), Some(30));
3623 assert_eq!(values[2].to_bool(), Some(true));
3624 }
3625
3626 #[test]
3627 fn test_mapping_values_different_types() {
3628 let yaml = YamlFile::from_str("string: hello\nnumber: 42\nbool: false").unwrap();
3629 let doc = yaml.document().unwrap();
3630 let mapping = doc.as_mapping().unwrap();
3631
3632 let values: Vec<_> = mapping.values().collect();
3634 assert_eq!(values.len(), 3);
3635
3636 assert_eq!(
3637 values[0].as_scalar().map(|s| s.as_string()),
3638 Some("hello".to_string())
3639 );
3640 assert_eq!(values[1].to_i64(), Some(42));
3641 assert_eq!(values[2].to_bool(), Some(false));
3642 }
3643
3644 #[test]
3645 fn test_mapping_iter_empty() {
3646 let yaml = YamlFile::from_str("{}").unwrap();
3647 let doc = yaml.document().unwrap();
3648 let mapping = doc.as_mapping().unwrap();
3649
3650 let pairs: Vec<_> = mapping.iter().collect();
3651 assert_eq!(pairs.len(), 0);
3652 }
3653
3654 #[test]
3655 fn test_mapping_iter_single() {
3656 let yaml = YamlFile::from_str("name: Alice").unwrap();
3657 let doc = yaml.document().unwrap();
3658 let mapping = doc.as_mapping().unwrap();
3659
3660 let pairs: Vec<_> = mapping.iter().collect();
3661 assert_eq!(pairs.len(), 1);
3662 assert_eq!(
3663 pairs[0].0.as_scalar().map(|s| s.as_string()),
3664 Some("name".to_string())
3665 );
3666 assert_eq!(
3667 pairs[0].1.as_scalar().map(|s| s.as_string()),
3668 Some("Alice".to_string())
3669 );
3670 }
3671
3672 #[test]
3673 fn test_mapping_iter_multiple() {
3674 let yaml = YamlFile::from_str("name: Alice\nage: 30\nactive: true").unwrap();
3675 let doc = yaml.document().unwrap();
3676 let mapping = doc.as_mapping().unwrap();
3677
3678 let pairs: Vec<_> = mapping.iter().collect();
3679
3680 assert_eq!(pairs.len(), 3);
3681 assert_eq!(
3682 pairs[0].0.as_scalar().map(|s| s.as_string()),
3683 Some("name".to_string())
3684 );
3685 assert_eq!(
3686 pairs[0].1.as_scalar().map(|s| s.as_string()),
3687 Some("Alice".to_string())
3688 );
3689 assert_eq!(
3690 pairs[1].0.as_scalar().map(|s| s.as_string()),
3691 Some("age".to_string())
3692 );
3693 assert_eq!(pairs[1].1.to_i64(), Some(30));
3694 assert_eq!(
3695 pairs[2].0.as_scalar().map(|s| s.as_string()),
3696 Some("active".to_string())
3697 );
3698 assert_eq!(pairs[2].1.to_bool(), Some(true));
3699 }
3700
3701 #[test]
3702 fn test_mapping_iter_different_types() {
3703 let yaml = YamlFile::from_str("string: hello\nnumber: 42\nbool: false").unwrap();
3704 let doc = yaml.document().unwrap();
3705 let mapping = doc.as_mapping().unwrap();
3706
3707 let pairs: Vec<_> = mapping.iter().collect();
3708 assert_eq!(pairs.len(), 3);
3709
3710 assert_eq!(
3712 pairs[0].0.as_scalar().map(|s| s.as_string()),
3713 Some("string".to_string())
3714 );
3715 assert_eq!(
3716 pairs[0].1.as_scalar().map(|s| s.as_string()),
3717 Some("hello".to_string())
3718 );
3719
3720 assert_eq!(
3722 pairs[1].0.as_scalar().map(|s| s.as_string()),
3723 Some("number".to_string())
3724 );
3725 assert_eq!(pairs[1].1.to_i64(), Some(42));
3726
3727 assert_eq!(
3729 pairs[2].0.as_scalar().map(|s| s.as_string()),
3730 Some("bool".to_string())
3731 );
3732 assert_eq!(pairs[2].1.to_bool(), Some(false));
3733 }
3734
3735 #[test]
3736 fn test_mapping_iter_preserves_order() {
3737 let yaml = YamlFile::from_str("z: 1\na: 2\nm: 3").unwrap();
3738 let doc = yaml.document().unwrap();
3739 let mapping = doc.as_mapping().unwrap();
3740
3741 let pairs: Vec<_> = mapping.iter().collect();
3742 assert_eq!(pairs.len(), 3);
3743 assert_eq!(
3744 pairs[0].0.as_scalar().map(|s| s.as_string()),
3745 Some("z".to_string())
3746 );
3747 assert_eq!(
3748 pairs[1].0.as_scalar().map(|s| s.as_string()),
3749 Some("a".to_string())
3750 );
3751 assert_eq!(
3752 pairs[2].0.as_scalar().map(|s| s.as_string()),
3753 Some("m".to_string())
3754 );
3755 }
3756
3757 #[test]
3758 fn test_mapping_clear_empty() {
3759 let yaml = YamlFile::from_str("{}").unwrap();
3760 let doc = yaml.document().unwrap();
3761 let mapping = doc.as_mapping().unwrap();
3762
3763 assert_eq!(mapping.len(), 0);
3764 mapping.clear();
3765 assert_eq!(mapping.len(), 0);
3766 }
3767
3768 #[test]
3769 fn test_mapping_clear_single() {
3770 let yaml = YamlFile::from_str("name: Alice").unwrap();
3771 let doc = yaml.document().unwrap();
3772 let mapping = doc.as_mapping().unwrap();
3773
3774 assert_eq!(mapping.len(), 1);
3775 mapping.clear();
3776 assert_eq!(mapping.len(), 0);
3777 assert!(mapping.is_empty());
3778
3779 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3780 assert_eq!(keys, Vec::<String>::new());
3781 }
3782
3783 #[test]
3784 fn test_mapping_clear_multiple() {
3785 let yaml = YamlFile::from_str("name: Alice\nage: 30\ncity: NYC").unwrap();
3786 let doc = yaml.document().unwrap();
3787 let mapping = doc.as_mapping().unwrap();
3788
3789 assert_eq!(mapping.len(), 3);
3790 mapping.clear();
3791 assert_eq!(mapping.len(), 0);
3792 assert!(mapping.is_empty());
3793
3794 let keys: Vec<String> = mapping.keys().map(|k| k.to_string()).collect();
3795 assert_eq!(keys, Vec::<String>::new());
3796 }
3797
3798 #[test]
3799 fn test_mapping_clear_and_add() {
3800 let yaml = YamlFile::from_str("name: Alice\nage: 30").unwrap();
3801 let doc = yaml.document().unwrap();
3802 let mapping = doc.as_mapping().unwrap();
3803
3804 assert_eq!(mapping.len(), 2);
3805 mapping.clear();
3806 assert_eq!(mapping.len(), 0);
3807
3808 mapping.set("new_key", "new_value");
3810 assert_eq!(mapping.len(), 1);
3811 let value = mapping.get("new_key").unwrap();
3812 assert_eq!(
3813 value.as_scalar().map(|s| s.as_string()),
3814 Some("new_value".to_string())
3815 );
3816 }
3817
3818 #[test]
3819 fn test_mapping_clear_large() {
3820 let yaml_str = (0..100)
3822 .map(|i| format!("key{}: value{}", i, i))
3823 .collect::<Vec<_>>()
3824 .join("\n");
3825 let yaml = YamlFile::from_str(&yaml_str).unwrap();
3826 let doc = yaml.document().unwrap();
3827 let mapping = doc.as_mapping().unwrap();
3828
3829 assert_eq!(mapping.len(), 100);
3830 mapping.clear();
3831 assert_eq!(mapping.len(), 0);
3832 assert!(mapping.is_empty());
3833 }
3834
3835 #[test]
3836 fn test_mapping_newline_handling_block_style() {
3837 let yaml_with_newline = "key1: value1\nkey2: value2\n";
3839 let yaml = YamlFile::from_str(yaml_with_newline).unwrap();
3840
3841 let output = yaml.to_string();
3843 assert!(
3844 output.ends_with('\n'),
3845 "Block-style mapping should preserve trailing newline"
3846 );
3847 assert_eq!(output, yaml_with_newline);
3848 }
3849
3850 #[test]
3851 fn test_mapping_newline_handling_no_trailing() {
3852 let yaml_no_newline = "key: value";
3854 let yaml = YamlFile::from_str(yaml_no_newline).unwrap();
3855
3856 let output = yaml.to_string();
3858 assert!(
3859 !output.ends_with('\n'),
3860 "Mapping without trailing newline should not add one"
3861 );
3862 assert_eq!(output, yaml_no_newline);
3863 }
3864
3865 #[test]
3866 fn test_mapping_newline_handling_flow_style() {
3867 let yaml_flow = "data: {key1: value1, key2: value2}";
3869 let yaml = YamlFile::from_str(yaml_flow).unwrap();
3870
3871 let output = yaml.to_string();
3873 assert_eq!(output, yaml_flow);
3874 }
3875
3876 #[test]
3877 fn test_mapping_set_preserves_newline_context() {
3878 let yaml_str = "key1: value1\nkey2: value2\n";
3880 let yaml = YamlFile::from_str(yaml_str).unwrap();
3881 let doc = yaml.document().unwrap();
3882 let mapping = doc.as_mapping().unwrap();
3883
3884 mapping.set("key1", "new_value");
3886
3887 let output = yaml.to_string();
3889 assert!(
3890 output.ends_with('\n'),
3891 "Newline should be preserved after modification"
3892 );
3893 }
3894}