1use super::{
2 append_children, ensure_trailing_newline, entry_indices, fresh_token, has_child_token,
3 nth_entry_index, Lang, SyntaxNode,
4};
5use crate::as_yaml::{AsYaml, YamlKind};
6use crate::lex::SyntaxKind;
7use crate::yaml::ValueNode;
8use rowan::ast::AstNode;
9use rowan::GreenNodeBuilder;
10
11ast_node!(Sequence, SEQUENCE, "A YAML sequence (list)");
12
13pub(crate) fn must_render_flow(node: &SyntaxNode) -> bool {
17 if node
18 .parent()
19 .is_some_and(|p| p.kind() == SyntaxKind::SEQUENCE_ENTRY)
20 {
21 return true;
22 }
23 let mut cur = node.parent();
24 while let Some(p) = cur {
25 let opener = match p.kind() {
26 SyntaxKind::MAPPING => Some(SyntaxKind::LEFT_BRACE),
27 SyntaxKind::SEQUENCE => Some(SyntaxKind::LEFT_BRACKET),
28 _ => None,
29 };
30 if let Some(open) = opener {
31 let pending =
35 crate::nodes::Mapping::cast(p.clone()).is_some_and(|m| m.is_block_placeholder());
36 if has_child_token(&p, |k| k == open) && !pending {
37 return true;
38 }
39 }
40 cur = p.parent();
41 }
42 false
43}
44
45fn parent_value_has_leading_indent(seq: &SyntaxNode) -> bool {
50 let Some(parent) = seq.parent() else {
51 return false;
52 };
53 if parent.kind() != SyntaxKind::VALUE {
54 return false;
55 }
56 let mut saw_indent = false;
57 for child in parent.children_with_tokens() {
58 match &child {
59 rowan::NodeOrToken::Node(n) if n == seq => return saw_indent,
60 rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::INDENT => saw_indent = true,
61 rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::NEWLINE => {}
62 _ => saw_indent = false,
63 }
64 }
65 false
66}
67
68fn trailing_newline_indent(node: &SyntaxNode) -> Option<String> {
76 let tokens: Vec<_> = node
77 .descendants_with_tokens()
78 .filter_map(|el| el.into_token())
79 .collect();
80 let mut result = String::new();
81 let last = tokens.last()?;
84 match last.kind() {
85 SyntaxKind::NEWLINE => {
86 result.push_str(last.text());
87 }
88 SyntaxKind::INDENT => {
89 let prev = tokens.iter().rev().nth(1)?;
90 if prev.kind() != SyntaxKind::NEWLINE {
91 return None;
92 }
93 result.push_str(prev.text());
94 result.push_str(last.text());
95 }
96 _ => return None,
97 }
98 Some(result)
99}
100
101fn rebuild_entry_with_value(entry: &SyntaxNode, value: &impl crate::AsYaml) -> SyntaxNode {
109 let entry_children: Vec<_> = entry.children_with_tokens().collect();
110
111 let mut builder = GreenNodeBuilder::new();
113 builder.start_node(SyntaxKind::SEQUENCE_ENTRY.into());
114
115 let mut value_inserted = false;
116 let mut trailing_text: Option<String> = None;
117 let mut after_dash = false;
118
119 for entry_child in entry_children {
120 match &entry_child {
121 rowan::NodeOrToken::Node(n)
122 if matches!(
123 n.kind(),
124 SyntaxKind::SCALAR
125 | SyntaxKind::MAPPING
126 | SyntaxKind::SEQUENCE
127 | SyntaxKind::ALIAS
128 | SyntaxKind::TAGGED_NODE
129 ) =>
130 {
131 trailing_text = trailing_newline_indent(n);
135 if !value_inserted {
136 if after_dash {
140 builder.token(SyntaxKind::WHITESPACE.into(), " ");
141 }
142 value.build_content(&mut builder, 0, false);
143 value_inserted = true;
144 }
145 after_dash = false;
146 }
147 rowan::NodeOrToken::Node(n) => {
148 crate::yaml::copy_node_to_builder(&mut builder, n);
150 after_dash = false;
151 }
152 rowan::NodeOrToken::Token(t) => {
153 builder.token(t.kind().into(), t.text());
155 after_dash = t.kind() == SyntaxKind::DASH;
156 }
157 }
158 }
159
160 if let Some(trailing) = trailing_text {
162 if let Some(indent_part) = trailing.strip_prefix('\n') {
163 builder.token(SyntaxKind::NEWLINE.into(), "\n");
164 if !indent_part.is_empty() {
165 builder.token(SyntaxKind::INDENT.into(), indent_part);
166 }
167 }
168 }
169
170 builder.finish_node();
171 SyntaxNode::new_root_mut(builder.finish())
172}
173
174fn strip_trailing_blank_tokens(entry: &SyntaxNode) {
177 let children: Vec<_> = entry.children_with_tokens().collect();
178 let blanks = children
179 .iter()
180 .rev()
181 .take_while(|c| {
182 c.as_token().is_some_and(|t| {
183 matches!(
184 t.kind(),
185 SyntaxKind::NEWLINE | SyntaxKind::INDENT | SyntaxKind::WHITESPACE
186 )
187 })
188 })
189 .count();
190 if blanks > 0 {
191 let total = children.len();
192 entry.splice_children((total - blanks)..total, vec![]);
193 }
194}
195
196fn mapping_entry_is_last_in_mapping(sequence: &SyntaxNode) -> bool {
197 let Some(value) = sequence.parent() else {
198 return false;
199 };
200 if value.kind() != SyntaxKind::VALUE {
201 return false;
202 }
203 let Some(entry) = value.parent() else {
204 return false;
205 };
206 if entry.kind() != SyntaxKind::MAPPING_ENTRY {
207 return false;
208 }
209 let Some(parent_mapping) = entry.parent() else {
210 return false;
211 };
212 if parent_mapping.kind() != SyntaxKind::MAPPING {
213 return false;
214 }
215 let mut seen_self = false;
218 for child in parent_mapping.children() {
219 if child == entry {
220 seen_self = true;
221 continue;
222 }
223 if seen_self && child.kind() == SyntaxKind::MAPPING_ENTRY {
224 return false;
225 }
226 }
227 true
228}
229
230use crate::yaml::collapse_empty_child_collection_in_parent as collapse_empty_child_sequence_in_parent;
233
234impl Sequence {
235 pub(crate) fn items(&self) -> impl Iterator<Item = SyntaxNode> + '_ {
240 self.0.children().filter_map(|child| {
241 if child.kind() == SyntaxKind::SEQUENCE_ENTRY {
242 child.children().find(|n| {
245 matches!(
246 n.kind(),
247 SyntaxKind::SCALAR
248 | SyntaxKind::MAPPING
249 | SyntaxKind::SEQUENCE
250 | SyntaxKind::ALIAS
251 | SyntaxKind::TAGGED_NODE
252 )
253 })
254 } else {
255 None
256 }
257 })
258 }
259
260 pub fn values(&self) -> impl Iterator<Item = crate::as_yaml::YamlNode> + '_ {
264 self.items()
265 .filter_map(crate::as_yaml::YamlNode::from_syntax)
266 }
267
268 pub fn len(&self) -> usize {
270 self.items().count()
271 }
272
273 pub fn is_empty(&self) -> bool {
275 self.items().next().is_none()
276 }
277
278 pub fn get(&self, index: usize) -> Option<crate::as_yaml::YamlNode> {
282 self.items()
283 .nth(index)
284 .and_then(crate::as_yaml::YamlNode::from_syntax)
285 }
286
287 pub fn first(&self) -> Option<crate::as_yaml::YamlNode> {
289 self.get(0)
290 }
291
292 pub fn last(&self) -> Option<crate::as_yaml::YamlNode> {
294 let len = self.len();
295 if len == 0 {
296 None
297 } else {
298 self.get(len - 1)
299 }
300 }
301}
302
303impl Sequence {
304 pub fn new() -> Self {
306 Self::new_pending_block()
307 }
308
309 pub fn new_flow() -> Self {
314 let mut builder = GreenNodeBuilder::new();
315 builder.start_node(SyntaxKind::SEQUENCE.into());
316 builder.token(SyntaxKind::LEFT_BRACKET.into(), "[");
317 builder.token(SyntaxKind::RIGHT_BRACKET.into(), "]");
318 builder.finish_node();
319 Sequence(SyntaxNode::new_root_mut(builder.finish()))
320 }
321
322 pub fn new_pending_block() -> Self {
339 let mut builder = GreenNodeBuilder::new();
340 builder.start_node(SyntaxKind::SEQUENCE.into());
341 builder.token(SyntaxKind::LEFT_BRACKET.into(), "[");
342 builder.token(SyntaxKind::WHITESPACE.into(), "");
346 builder.token(SyntaxKind::RIGHT_BRACKET.into(), "]");
347 builder.finish_node();
348 Sequence(SyntaxNode::new_root_mut(builder.finish()))
349 }
350
351 pub(crate) fn is_block_placeholder(&self) -> bool {
354 !self
355 .0
356 .children()
357 .any(|c| c.kind() == SyntaxKind::SEQUENCE_ENTRY)
358 && self
359 .0
360 .children_with_tokens()
361 .filter_map(|c| c.into_token())
362 .any(|t| t.kind() == SyntaxKind::WHITESPACE && t.text().is_empty())
363 }
364
365 fn detect_indentation(&self) -> String {
371 if let Some(ind) = self.0.children_with_tokens().find_map(|child| {
373 child
374 .into_token()
375 .filter(|t| t.kind() == SyntaxKind::INDENT)
376 .map(|t| t.text().to_string())
377 }) {
378 return ind;
379 }
380
381 if let Some(indent) = self
383 .0
384 .children()
385 .filter(|c| c.kind() == SyntaxKind::SEQUENCE_ENTRY)
386 .find_map(|entry| {
387 let tokens: Vec<_> = entry.children_with_tokens().collect();
388 tokens.windows(2).find_map(|pair| {
389 let ws = pair[0].as_token()?;
390 let dash = pair[1].as_token()?;
391 if ws.kind() == SyntaxKind::WHITESPACE && dash.kind() == SyntaxKind::DASH {
392 Some(ws.text().to_string())
393 } else {
394 None
395 }
396 })
397 })
398 {
399 return indent;
400 }
401
402 if let Some(parent) = self.0.parent().filter(|p| p.kind() == SyntaxKind::VALUE) {
405 for child in parent.children_with_tokens() {
406 match &child {
407 rowan::NodeOrToken::Node(n) if n == &self.0 => break,
408 rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::INDENT => {
409 return t.text().to_string();
410 }
411 _ => {}
412 }
413 }
414 }
415
416 match self.0.parent() {
421 None => return String::new(),
422 Some(parent) if parent.kind() == SyntaxKind::DOCUMENT => return String::new(),
423 Some(_) => {}
424 }
425
426 " ".to_string()
427 }
428
429 pub(crate) fn extend_with(&self, value: impl crate::AsYaml + Clone, count: usize) {
437 if count == 0 {
438 return;
439 }
440 self.push(value.clone());
445 let Some(remaining) = count.checked_sub(1).filter(|n| *n > 0) else {
446 return;
447 };
448 if self.is_flow_style() {
449 for _ in 0..remaining {
450 self.push(value.clone());
451 }
452 return;
453 }
454
455 let indentation = self.detect_indentation();
458 let mut inserts: Vec<rowan::NodeOrToken<SyntaxNode, _>> = Vec::new();
459 for _ in 0..remaining {
460 let mut builder = GreenNodeBuilder::new();
461 builder.start_node(SyntaxKind::SEQUENCE_ENTRY.into());
462 builder.token(SyntaxKind::DASH.into(), "-");
463 builder.token(SyntaxKind::WHITESPACE.into(), " ");
464 let ends_with_newline = value.clone().build_content(&mut builder, 0, false);
465 if !ends_with_newline {
466 builder.token(SyntaxKind::NEWLINE.into(), "\n");
467 }
468 builder.finish_node();
469 inserts.push(fresh_token(SyntaxKind::INDENT, &indentation).into());
470 inserts.push(SyntaxNode::new_root_mut(builder.finish()).into());
471 }
472
473 let end = self.0.green().children().len();
474 self.0.splice_children(end..end, inserts);
475 }
476
477 pub fn push(&self, value: impl crate::AsYaml) {
498 if self.is_block_placeholder() && !must_render_flow(&self.0) {
503 crate::yaml::convert_placeholder_to_block(&self.0);
504 }
505 if self.is_flow_style() {
506 self.insert_flow(usize::MAX, value);
507 return;
508 }
509
510 let indentation = self.detect_indentation();
511
512 let indent_token = fresh_token(SyntaxKind::INDENT, &indentation);
514
515 let green = self.0.green();
525 let mut tail = green.children();
526 let child_count = tail.len();
527
528 let mut last_entry_index = None;
533 let mut last_entry_has_newline = true; let mut insert_pos = child_count;
535 let mut idx = child_count;
536 while idx > 0 {
537 idx -= 1;
538 match tail.next_back() {
539 Some(rowan::NodeOrToken::Node(n))
540 if n.kind() == SyntaxKind::SEQUENCE_ENTRY.into() =>
541 {
542 last_entry_has_newline = n
543 .children()
544 .next_back()
545 .and_then(|c| c.into_token())
546 .is_some_and(|t| t.kind() == SyntaxKind::NEWLINE.into());
547 last_entry_index = Some(idx);
548 if insert_pos == child_count {
549 insert_pos = idx + 1;
550 }
551 break;
552 }
553 Some(rowan::NodeOrToken::Token(tok)) if tok.kind() == SyntaxKind::INDENT.into() => {
554 insert_pos = idx + 1;
555 }
556 Some(rowan::NodeOrToken::Token(tok))
557 if tok.kind() == SyntaxKind::NEWLINE.into() =>
558 {
559 insert_pos = idx;
560 }
561 _ => break,
562 }
563 }
564
565 let mut builder = GreenNodeBuilder::new();
567 builder.start_node(SyntaxKind::SEQUENCE_ENTRY.into());
568 builder.token(SyntaxKind::DASH.into(), "-");
569 builder.token(SyntaxKind::WHITESPACE.into(), " ");
570
571 let value_ends_with_newline = value.build_content(&mut builder, 0, false);
573
574 if last_entry_has_newline && !value_ends_with_newline {
577 builder.token(SyntaxKind::NEWLINE.into(), "\n");
578 }
579 builder.finish_node(); let new_entry = SyntaxNode::new_root_mut(builder.finish());
581
582 if last_entry_index.is_some() && !last_entry_has_newline {
585 if let Some(node) = self
586 .0
587 .last_child()
588 .filter(|n| n.kind() == SyntaxKind::SEQUENCE_ENTRY)
589 {
590 let entry_children_count = node.green().children().len();
591 let nl = fresh_token(SyntaxKind::NEWLINE, "\n");
592 node.splice_children(entry_children_count..entry_children_count, vec![nl.into()]);
593 }
594 }
595
596 let entry_is_first = last_entry_index.is_none();
602 let parent_supplies_indent = entry_is_first && parent_value_has_leading_indent(&self.0);
603
604 let mut inserts: Vec<rowan::NodeOrToken<SyntaxNode, _>> = Vec::new();
605 if !parent_supplies_indent {
606 inserts.push(indent_token.into());
607 }
608 inserts.push(new_entry.clone().into());
609
610 self.0.splice_children(insert_pos..insert_pos, inserts);
611
612 if entry_is_first {
615 crate::yaml::detach_empty_collection_placeholder_newline(&self.0, &new_entry);
616 }
617 }
618
619 fn insert_flow(&self, index: usize, value: impl crate::AsYaml) {
625 let mut builder = GreenNodeBuilder::new();
626 builder.start_node(SyntaxKind::SEQUENCE_ENTRY.into());
627 value.build_content(&mut builder, 0, false);
630 builder.finish_node();
631 let new_entry = SyntaxNode::new_root_mut(builder.finish());
632
633 let children: Vec<_> = self.0.children_with_tokens().collect();
634 let entry_positions = entry_indices(&self.0, SyntaxKind::SEQUENCE_ENTRY);
635 let Some(right_bracket_pos) = children.iter().position(|c| {
636 c.as_token()
637 .is_some_and(|t| t.kind() == SyntaxKind::RIGHT_BRACKET)
638 }) else {
639 debug_assert!(false, "flow SEQUENCE missing RIGHT_BRACKET");
644 return;
645 };
646
647 let comma = fresh_token(SyntaxKind::COMMA, ",");
648 let sep_ws = fresh_token(SyntaxKind::WHITESPACE, " ");
649
650 if index >= entry_positions.len() {
651 if let Some(&last_pos) = entry_positions.last() {
654 let last_entry = children[last_pos].as_node().expect("SEQUENCE_ENTRY");
655 let ends_with_comma = last_entry
656 .last_token()
657 .is_some_and(|t| t.kind() == SyntaxKind::COMMA);
658 if !ends_with_comma {
659 append_children(last_entry, vec![comma.into(), sep_ws.into()]);
660 }
661 }
662 self.0
663 .splice_children(right_bracket_pos..right_bracket_pos, vec![new_entry.into()]);
664 return;
665 }
666
667 let target_pos = entry_positions[index];
670 append_children(&new_entry, vec![comma.into(), sep_ws.into()]);
671 self.0
672 .splice_children(target_pos..target_pos, vec![new_entry.into()]);
673 }
674
675 pub fn insert(&self, index: usize, value: impl crate::AsYaml) {
682 if self.is_block_placeholder() && !must_render_flow(&self.0) {
684 crate::yaml::convert_placeholder_to_block(&self.0);
685 }
686 if self.is_flow_style() {
687 self.insert_flow(index, value);
688 return;
689 }
690
691 let indentation = self.detect_indentation();
692
693 let mut builder = GreenNodeBuilder::new();
697 builder.start_node(SyntaxKind::SEQUENCE_ENTRY.into());
698 builder.token(SyntaxKind::DASH.into(), "-");
699 builder.token(SyntaxKind::WHITESPACE.into(), " ");
700 let value_ends_with_newline = value.build_content(&mut builder, 0, false);
701 if !value_ends_with_newline {
702 builder.token(SyntaxKind::NEWLINE.into(), "\n");
703 }
704 builder.finish_node();
705 let new_entry = SyntaxNode::new_root_mut(builder.finish());
706
707 let indent_token = fresh_token(SyntaxKind::INDENT, &indentation);
709
710 let children: Vec<_> = self.0.children_with_tokens().collect();
715 let mut item_count = 0;
716 let mut target_entry_pos = children.len();
717 for (i, child) in children.iter().enumerate() {
718 let Some(node) = child.as_node() else {
719 continue;
720 };
721 if node.kind() != SyntaxKind::SEQUENCE_ENTRY {
722 continue;
723 }
724 if item_count == index {
725 target_entry_pos = i;
726 break;
727 }
728 item_count += 1;
729 }
730
731 let insert_at = if target_entry_pos > 0
736 && children
737 .get(target_entry_pos - 1)
738 .and_then(|c| c.as_token())
739 .is_some_and(|t| t.kind() == SyntaxKind::INDENT)
740 {
741 target_entry_pos - 1
742 } else {
743 target_entry_pos
744 };
745
746 if target_entry_pos == children.len() {
750 if let Some(prev_entry) = children.iter().rev().find_map(|c| {
751 c.as_node()
752 .filter(|n| n.kind() == SyntaxKind::SEQUENCE_ENTRY)
753 }) {
754 ensure_trailing_newline(prev_entry);
755 }
756 }
757
758 let inserting_at_head = target_entry_pos < children.len()
764 && children[..target_entry_pos].iter().all(|c| {
765 c.as_node()
766 .map_or(true, |n| n.kind() != SyntaxKind::SEQUENCE_ENTRY)
767 });
768 if inserting_at_head && parent_value_has_leading_indent(&self.0) {
769 let old_first_indent = fresh_token(SyntaxKind::INDENT, &indentation);
770 self.0.splice_children(
771 insert_at..insert_at,
772 vec![new_entry.into(), old_first_indent.into()],
773 );
774 return;
775 }
776
777 self.0.splice_children(
778 insert_at..insert_at,
779 vec![indent_token.into(), new_entry.into()],
780 );
781 }
782
783 pub fn set(&self, index: usize, value: impl crate::AsYaml) -> bool {
790 let children: Vec<_> = self.0.children_with_tokens().collect();
791
792 let Some(i) = nth_entry_index(&self.0, SyntaxKind::SEQUENCE_ENTRY, index) else {
793 return false;
794 };
795 let node = children[i].as_node().expect("entry index names a node");
796 let new_entry = rebuild_entry_with_value(node, &value);
797
798 self.0.splice_children(i..i + 1, vec![new_entry.into()]);
800 true
801 }
802
803 fn detach_orphaned_indent(
812 &self,
813 children: &[rowan::NodeOrToken<SyntaxNode, rowan::SyntaxToken<Lang>>],
814 i: usize,
815 ) {
816 if self.is_flow_style() {
817 return;
818 }
819 let (neighbour, range) = if i > 0 {
820 (children.get(i - 1), (i - 1)..i)
821 } else {
822 (children.get(i + 1), i..(i + 1))
823 };
824 let is_indent =
825 neighbour.is_some_and(|c| c.as_token().is_some_and(|t| t.kind() == SyntaxKind::INDENT));
826 if is_indent {
827 self.0.splice_children(range, vec![]);
828 }
829 }
830
831 pub fn remove(&self, index: usize) -> Option<crate::as_yaml::YamlNode> {
837 let removed_value = self.get(index);
839
840 let children: Vec<_> = self.0.children_with_tokens().collect();
842
843 let i = children
846 .iter()
847 .enumerate()
848 .filter(|(_, c)| {
849 c.as_node()
850 .is_some_and(|n| n.kind() == SyntaxKind::SEQUENCE_ENTRY)
851 })
852 .map(|(i, _)| i)
853 .nth(index)?;
854 let is_last = !children.iter().skip(i + 1).any(|c| {
855 c.as_node()
856 .is_some_and(|n| n.kind() == SyntaxKind::SEQUENCE_ENTRY)
857 });
858
859 self.0.splice_children(i..(i + 1), vec![]);
871 self.detach_orphaned_indent(&children, i);
872
873 if !self.is_flow_style() && is_last && i > 0 && mapping_entry_is_last_in_mapping(&self.0) {
874 if let Some(prev_entry) = children[..i].iter().rev().find_map(|c| {
885 c.as_node()
886 .filter(|n| n.kind() == SyntaxKind::SEQUENCE_ENTRY)
887 }) {
888 strip_trailing_blank_tokens(prev_entry);
889 }
890 }
891 if self.is_empty() {
896 collapse_empty_child_sequence_in_parent(&self.0);
897 }
898 removed_value
899 }
900
901 pub fn is_flow_style(&self) -> bool {
903 has_child_token(&self.0, |k| k == SyntaxKind::LEFT_BRACKET)
904 }
905
906 pub fn pop(&self) -> Option<crate::as_yaml::YamlNode> {
912 let len = self.len();
913 if len == 0 {
914 return None;
915 }
916 let removed = self.remove(len - 1);
917
918 debug_assert_eq!(
919 self.len(),
920 len - 1,
921 "pop() invariant: remove() did not reduce length"
922 );
923
924 removed
925 }
926
927 pub fn clear(&self) {
931 let initial_len = self.len();
934 for _ in 0..initial_len {
935 let current_len = self.len();
936 if current_len == 0 {
937 break;
938 }
939 let removed = self.remove(0);
941 debug_assert!(
942 removed.is_some(),
943 "clear() invariant: remove(0) returned None"
944 );
945 debug_assert_eq!(
946 self.len(),
947 current_len - 1,
948 "clear() invariant: remove(0) did not reduce length"
949 );
950 }
951 }
952
953 crate::nodes::ast_node_spans!("sequence");
954}
955
956impl Default for Sequence {
957 fn default() -> Self {
958 Self::new()
959 }
960}
961
962impl<'a> IntoIterator for &'a Sequence {
965 type Item = crate::as_yaml::YamlNode;
966 type IntoIter = Box<dyn Iterator<Item = crate::as_yaml::YamlNode> + 'a>;
967
968 fn into_iter(self) -> Self::IntoIter {
969 Box::new(self.values())
970 }
971}
972
973impl AsYaml for Sequence {
974 fn as_node(&self) -> Option<&SyntaxNode> {
975 Some(&self.0)
976 }
977
978 fn kind(&self) -> YamlKind {
979 YamlKind::Sequence
980 }
981
982 fn build_content(
983 &self,
984 builder: &mut rowan::GreenNodeBuilder,
985 indent: usize,
986 _flow_context: bool,
987 ) -> bool {
988 builder.start_node(SyntaxKind::SEQUENCE.into());
989 crate::as_yaml::copy_node_content_with_indent(builder, &self.0, indent);
990 builder.finish_node();
991 self.0
992 .last_token()
993 .is_some_and(|t| t.kind() == SyntaxKind::NEWLINE)
994 }
995
996 fn is_inline(&self) -> bool {
997 ValueNode::is_inline(self)
998 }
999}
1000#[cfg(test)]
1001mod tests {
1002 use crate::yaml::YamlFile;
1003 use std::str::FromStr;
1004
1005 #[test]
1010 fn pending_block_sequence_round_trips_and_becomes_block() {
1011 use crate::yaml::Sequence;
1012 use crate::Document;
1013 use std::str::FromStr;
1014
1015 let doc = Document::from_str("a: 1\n").unwrap();
1016 doc.as_mapping()
1017 .unwrap()
1018 .set("s", Sequence::new_pending_block());
1019 let text = doc.to_string();
1020 assert_eq!(text, "a: 1\ns: []\n");
1021
1022 let reparsed = Document::from_str(&text).unwrap();
1023 let value = reparsed.as_mapping().and_then(|m| m.get("s")).unwrap();
1024 assert!(
1025 value.as_sequence().is_some(),
1026 "empty sequence must survive as a sequence"
1027 );
1028
1029 let doc = Document::from_str("a: 1\n").unwrap();
1030 let root = doc.as_mapping().unwrap();
1031 root.set("s", Sequence::new_pending_block());
1032 let nested = root.get("s").unwrap();
1033 nested.as_sequence().unwrap().push("x");
1034 assert_eq!(doc.to_string(), "a: 1\ns:\n - x\n");
1035 }
1036
1037 #[test]
1040 fn parsed_flow_sequence_stays_flow_when_filled() {
1041 use crate::yaml::Sequence;
1042 use crate::Document;
1043 use std::str::FromStr;
1044
1045 let doc = Document::from_str("a: 1\n").unwrap();
1046 let root = doc.as_mapping().unwrap();
1047 root.set("s", Sequence::new_flow());
1048 let nested = root.get("s").unwrap();
1049 nested.as_sequence().unwrap().push("x");
1050 assert_eq!(doc.to_string(), "a: 1\ns: [x]\n");
1051 }
1052
1053 #[test]
1056 fn set_path_builds_block_sequence_intermediates() {
1057 use crate::path::YamlPath;
1058 use crate::Document;
1059 use std::str::FromStr;
1060
1061 let doc = Document::from_str("name: test\n").unwrap();
1062 doc.try_set_path("a.b[0]", "v").unwrap();
1063 assert_eq!(doc.to_string(), "name: test\na:\n b:\n - v\n");
1064 }
1065
1066 #[test]
1069 fn extend_with_matches_a_push_loop() {
1070 use crate::yaml::Document;
1071 use std::str::FromStr;
1072
1073 for src in [
1074 "s:\n - a\n",
1075 "s: []\n",
1076 "s: [1]\n",
1077 "s:\n - x\n - y\n",
1078 "s:\n - deep\n",
1079 "s:\n - a\nafter: kept\n",
1080 ] {
1081 for count in 0..6usize {
1082 let pushed = {
1083 let doc = Document::from_str(src).unwrap();
1084 let seq = doc.as_mapping().unwrap().get_sequence("s").unwrap();
1085 for _ in 0..count {
1086 seq.push(crate::scalar::ScalarValue::null());
1087 }
1088 doc.to_string()
1089 };
1090 let extended = {
1091 let doc = Document::from_str(src).unwrap();
1092 let seq = doc.as_mapping().unwrap().get_sequence("s").unwrap();
1093 seq.extend_with(crate::scalar::ScalarValue::null(), count);
1094 doc.to_string()
1095 };
1096 assert_eq!(extended, pushed, "src={src:?} count={count}");
1097 }
1098 }
1099 }
1100
1101 #[test]
1102 fn test_push_into_empty_flow_sequence_reshapes_to_block() {
1103 use crate::path::YamlPath;
1108 use crate::Document;
1109 let doc = Document::from_str("seq: []").unwrap();
1110 let seq = doc
1111 .try_get_path("seq")
1112 .unwrap()
1113 .as_sequence()
1114 .unwrap()
1115 .clone();
1116 seq.push("item1");
1117 assert_eq!(doc.to_string(), "seq: [item1]");
1118 }
1119
1120 #[test]
1121 fn test_push_into_empty_flow_sequence_nested_indent() {
1122 use crate::path::YamlPath;
1123 use crate::Document;
1124 let doc = Document::from_str("a:\n seq: []\n").unwrap();
1125 let seq = doc
1126 .try_get_path("a.seq")
1127 .unwrap()
1128 .as_sequence()
1129 .unwrap()
1130 .clone();
1131 seq.push("item1");
1132 assert_eq!(doc.to_string(), "a:\n seq: [item1]\n");
1133 }
1134
1135 #[test]
1136 fn test_push_deeply_nested_block_sequence_inherits_indent() {
1137 use crate::path::YamlPath;
1142 use crate::Document;
1143 let doc = Document::from_str("a:\n b:\n c:\n - existing\n").unwrap();
1144 let seq = doc
1145 .try_get_path("a.b.c")
1146 .unwrap()
1147 .as_sequence()
1148 .unwrap()
1149 .clone();
1150 seq.push("new_item");
1151 assert_eq!(
1152 doc.to_string(),
1153 "a:\n b:\n c:\n - existing\n - new_item\n"
1154 );
1155 }
1156
1157 #[test]
1158 fn test_push_into_empty_sequence_under_mapping_placeholder() {
1159 use crate::{Document, Sequence};
1164 let doc = Document::from_str("existing: value\n").unwrap();
1165 let mapping = doc.as_mapping().unwrap();
1166 mapping.set("items", Sequence::new());
1167 let items = mapping.get_sequence("items").unwrap();
1168 items.push("apple");
1169 items.push("banana");
1170 assert_eq!(
1171 doc.to_string(),
1172 "existing: value\nitems:\n - apple\n - banana\n"
1173 );
1174 }
1175
1176 #[test]
1177 fn test_implicit_null_item_shares_indexes_with_set_and_remove() {
1178 use crate::Document;
1179 let doc = Document::from_str("- a\n- \n- c\n").unwrap();
1180 let seq = doc.as_sequence().unwrap();
1181 assert_eq!(seq.len(), 3);
1182 assert_eq!(seq.get(0).unwrap().as_scalar().unwrap().as_string(), "a");
1183 assert_eq!(seq.get(1).unwrap().as_scalar().unwrap().as_string(), "");
1184 assert_eq!(seq.get(2).unwrap().as_scalar().unwrap().as_string(), "c");
1185
1186 assert!(seq.set(1, "x"));
1187 assert_eq!(doc.to_string(), "- a\n- x\n- c\n");
1188
1189 let doc = Document::from_str("- a\n- \n- c\n").unwrap();
1190 let seq = doc.as_sequence().unwrap();
1191 let removed = seq.remove(1);
1192 assert_eq!(
1193 removed.and_then(|n| n.as_scalar().map(|s| s.as_string())),
1194 Some(String::new())
1195 );
1196 assert_eq!(seq.len(), 2);
1197 assert_eq!(seq.get(1).unwrap().as_scalar().unwrap().as_string(), "c");
1198
1199 let doc = Document::from_str("- a\n-\n- c\n").unwrap();
1200 let seq = doc.as_sequence().unwrap();
1201 assert_eq!(seq.len(), 3);
1202 assert!(seq.set(1, "x"));
1203 assert_eq!(doc.to_string(), "- a\n- x\n- c\n");
1204 }
1205
1206 #[test]
1207 fn test_flow_sequence_implicit_null_shares_indexes() {
1208 use crate::debug::{roundtrip_ok, validate_tree};
1209 use crate::{AsYaml, Document};
1210
1211 let doc = Document::from_str("[a, , c]").unwrap();
1215 let seq = doc.as_sequence().unwrap();
1216 assert_eq!(seq.len(), 3);
1217 assert_eq!(seq.get(0).unwrap().as_scalar().unwrap().as_string(), "a");
1218 assert_eq!(seq.get(1).unwrap().as_scalar().unwrap().as_string(), "");
1219 assert_eq!(seq.get(2).unwrap().as_scalar().unwrap().as_string(), "c");
1220 validate_tree(doc.as_node().unwrap()).unwrap();
1221 roundtrip_ok(doc.as_node().unwrap()).unwrap();
1222
1223 assert!(seq.set(1, "x"));
1224 assert_eq!(doc.to_string(), "[a, x, c]");
1225
1226 let doc = Document::from_str("[a, , c]").unwrap();
1227 let seq = doc.as_sequence().unwrap();
1228 let removed = seq.remove(1);
1229 assert_eq!(
1230 removed.and_then(|n| n.as_scalar().map(|s| s.as_string())),
1231 Some(String::new())
1232 );
1233 assert_eq!(seq.len(), 2);
1234 assert_eq!(doc.to_string(), "[a, c]");
1235 }
1236
1237 #[test]
1238 fn test_flow_sequence_trailing_comma_is_not_extra_entry() {
1239 use crate::Document;
1240
1241 let doc = Document::from_str("[a, b,]").unwrap();
1244 let seq = doc.as_sequence().unwrap();
1245 assert_eq!(seq.len(), 2);
1246 assert_eq!(seq.get(0).unwrap().as_scalar().unwrap().as_string(), "a");
1247 assert_eq!(seq.get(1).unwrap().as_scalar().unwrap().as_string(), "b");
1248 assert_eq!(doc.to_string(), "[a, b,]");
1249 }
1250
1251 #[test]
1252 fn test_flow_sequence_only_nulls() {
1253 use crate::debug::{roundtrip_ok, validate_tree};
1254 use crate::{AsYaml, Document};
1255
1256 let doc = Document::from_str("[,]").unwrap();
1258 assert_eq!(doc.as_sequence().unwrap().len(), 1);
1259 validate_tree(doc.as_node().unwrap()).unwrap();
1260 roundtrip_ok(doc.as_node().unwrap()).unwrap();
1261
1262 let doc = Document::from_str("[,,]").unwrap();
1263 assert_eq!(doc.as_sequence().unwrap().len(), 2);
1264 validate_tree(doc.as_node().unwrap()).unwrap();
1265 roundtrip_ok(doc.as_node().unwrap()).unwrap();
1266 }
1267
1268 #[test]
1269 fn test_sequence_items_tagged_node() {
1270 let yaml = "- !custom foo\n- !custom bar\n- plain\n";
1273 let parsed = YamlFile::from_str(yaml).unwrap();
1274
1275 let doc = parsed.document().unwrap();
1276 let seq = doc.as_sequence().unwrap();
1277 assert_eq!(
1278 seq.items().count(),
1279 3,
1280 "Tagged scalars should be included in items()"
1281 );
1282 assert_eq!(
1284 seq.values().count(),
1285 3,
1286 "Tagged scalars should be included in values()"
1287 );
1288 }
1289
1290 #[test]
1291 fn test_sequence_set_tagged_node() {
1292 let yaml = "- !custom foo\n- bar\n";
1296 let parsed = YamlFile::from_str(yaml).unwrap();
1297 let doc = parsed.document().unwrap();
1298 let seq = doc.as_sequence().unwrap();
1299
1300 seq.set(0, "replaced");
1301
1302 let values: Vec<_> = seq.values().collect();
1303 assert_eq!(values.len(), 2);
1304 assert_eq!(
1305 values[0].as_scalar().map(|s| s.as_string()),
1306 Some("replaced".to_string())
1307 );
1308 assert_eq!(
1309 values[1].as_scalar().map(|s| s.as_string()),
1310 Some("bar".to_string())
1311 );
1312 }
1313
1314 #[test]
1315 fn test_sequence_set_alias() {
1316 let yaml = "colors:\n - *red\n - keep\n";
1317 let parsed = YamlFile::from_str(yaml).unwrap();
1318 let doc = parsed.document().unwrap();
1319 let mapping = doc.as_mapping().unwrap();
1320 let colors_node = mapping.get("colors").unwrap();
1321 let seq = colors_node.as_sequence().unwrap();
1322
1323 assert!(seq.set(0, "blue"));
1324 assert_eq!(parsed.to_string(), "colors:\n - blue\n - keep\n");
1325
1326 let first = seq.get(0).unwrap();
1327 assert_eq!(
1328 first.as_scalar().map(|s| s.as_string()),
1329 Some("blue".to_string())
1330 );
1331 }
1332
1333 #[test]
1334 fn test_sequence_operations() {
1335 let yaml = "- item1\n- item2";
1336 let parsed = YamlFile::from_str(yaml).unwrap();
1337
1338 let doc = parsed.document().expect("expected a document");
1339 let seq = doc.as_sequence().expect("expected a sequence");
1340
1341 seq.push("item3");
1343 let values: Vec<_> = seq.values().collect();
1344 assert_eq!(values.len(), 3);
1345 assert_eq!(
1346 values[2].as_scalar().map(|s| s.as_string()),
1347 Some("item3".to_string())
1348 );
1349
1350 seq.insert(0, "item0");
1352 let values: Vec<_> = seq.values().collect();
1353 assert_eq!(values.len(), 4);
1354 assert_eq!(
1355 values[0].as_scalar().map(|s| s.as_string()),
1356 Some("item0".to_string())
1357 );
1358 }
1359
1360 #[test]
1363 fn test_sequence_into_iterator() {
1364 use crate::Document;
1365 let text = "items:\n - apple\n - banana\n - cherry";
1366 let doc = Document::from_str(text).unwrap();
1367 let mapping = doc.as_mapping().unwrap();
1368 let sequence = mapping.get_sequence("items").unwrap();
1369
1370 let mut items = Vec::new();
1372 for value in &sequence {
1373 if let Some(scalar) = value.as_scalar() {
1374 items.push(scalar.to_string());
1375 }
1376 }
1377
1378 assert_eq!(items.len(), 3);
1379 assert_eq!(items[0], "apple");
1380 assert_eq!(items[1], "banana");
1381 assert_eq!(items[2], "cherry");
1382 }
1383
1384 #[test]
1385 fn test_sequence_into_iterator_count() {
1386 use crate::Document;
1387 let text = "[1, 2, 3, 4, 5]";
1388 let doc = Document::from_str(text).unwrap();
1389 let sequence = doc.as_sequence().unwrap();
1390
1391 let count = (&sequence).into_iter().count();
1392 assert_eq!(count, 5);
1393 }
1394
1395 #[test]
1396 fn test_sequence_iterator_map() {
1397 use crate::Document;
1398 let text = "numbers: [1, 2, 3]";
1399 let doc = Document::from_str(text).unwrap();
1400 let mapping = doc.as_mapping().unwrap();
1401 let sequence = mapping.get_sequence("numbers").unwrap();
1402
1403 let strings: Vec<_> = (&sequence)
1405 .into_iter()
1406 .filter_map(|v| v.as_scalar().map(|s| s.to_string()))
1407 .collect();
1408
1409 assert_eq!(strings, vec!["1", "2", "3"]);
1410 }
1411
1412 #[test]
1413 fn test_empty_sequence_iterator() {
1414 use crate::Document;
1415 let text = "items: []";
1416 let doc = Document::from_str(text).unwrap();
1417 let mapping = doc.as_mapping().unwrap();
1418 let sequence = mapping.get_sequence("items").unwrap();
1419
1420 let count = (&sequence).into_iter().count();
1421 assert_eq!(count, 0);
1422 }
1423
1424 #[test]
1427 fn test_sequence_push_single() {
1428 use crate::Document;
1429 let original = r#"team:
1430 - Alice
1431 - Bob"#;
1432
1433 let doc = Document::from_str(original).unwrap();
1434 let mapping = doc.as_mapping().unwrap();
1435 let team = mapping.get_sequence("team").unwrap();
1436 team.push("Charlie");
1437
1438 let expected = r#"team:
1439 - Alice
1440 - Bob
1441 - Charlie"#;
1442 assert_eq!(doc.to_string(), expected);
1443 }
1444
1445 #[test]
1446 fn test_sequence_push_multiple() {
1447 use crate::Document;
1448 let original = r#"team:
1449 - Alice
1450 - Bob"#;
1451
1452 let doc = Document::from_str(original).unwrap();
1453 let mapping = doc.as_mapping().unwrap();
1454 let team = mapping.get_sequence("team").unwrap();
1455 team.push("Charlie");
1456 team.push("Diana");
1457
1458 let expected = r#"team:
1459 - Alice
1460 - Bob
1461 - Charlie
1462 - Diana"#;
1463 assert_eq!(doc.to_string(), expected);
1464 }
1465
1466 #[test]
1467 fn test_sequence_set_item() {
1468 use crate::Document;
1469 let original = r#"team:
1470 - Alice
1471 - Bob
1472 - Charlie"#;
1473
1474 let doc = Document::from_str(original).unwrap();
1475 let mapping = doc.as_mapping().unwrap();
1476 let team = mapping.get_sequence("team").unwrap();
1477 team.set(1, "Robert");
1478
1479 let expected = r#"team:
1480 - Alice
1481 - Robert
1482 - Charlie"#;
1483 assert_eq!(doc.to_string(), expected);
1484 }
1485
1486 #[test]
1487 fn test_multiple_sequences() {
1488 use crate::Document;
1489 let original = r#"team:
1490 - Alice
1491 - Bob
1492
1493scores:
1494 - 95
1495 - 87"#;
1496
1497 let doc = Document::from_str(original).unwrap();
1498 let mapping = doc.as_mapping().unwrap();
1499 let team = mapping.get_sequence("team").unwrap();
1500 team.push("Charlie");
1501 let scores = mapping.get_sequence("scores").unwrap();
1502 scores.push(92);
1503 scores.set(0, 100);
1504
1505 let expected = r#"team:
1506 - Alice
1507 - Bob
1508 - Charlie
1509
1510scores:
1511 - 100
1512 - 87
1513 - 92"#;
1514 assert_eq!(doc.to_string(), expected);
1515 }
1516
1517 #[test]
1518 fn test_nested_structure_with_sequences() {
1519 use crate::Document;
1520 let original = r#"config:
1521 enabled: true
1522 retries: 3
1523 servers:
1524 - host1
1525 - host2"#;
1526
1527 let doc = Document::from_str(original).unwrap();
1528 let mapping = doc.as_mapping().unwrap();
1529 let config = mapping.get_mapping("config").unwrap();
1530 config.set("enabled", false);
1531 config.set("retries", 5);
1532
1533 let servers = config.get_sequence("servers").unwrap();
1534 servers.push("host3");
1535 servers.set(0, "primary-host");
1536
1537 let expected = r#"config:
1538 enabled: false
1539 retries: 5
1540 servers:
1541 - primary-host
1542 - host2
1543 - host3"#;
1544 assert_eq!(doc.to_string(), expected);
1545 }
1546
1547 #[test]
1548 fn test_sequence_len_and_is_empty() {
1549 use crate::Document;
1550 let doc = Document::from_str("items:\n - a\n - b\n - c").unwrap();
1551 let mapping = doc.as_mapping().unwrap();
1552 let seq = mapping.get_sequence("items").unwrap();
1553
1554 assert_eq!(seq.len(), 3);
1555 assert!(!seq.is_empty());
1556
1557 let empty_doc = Document::from_str("items: []").unwrap();
1558 let empty_mapping = empty_doc.as_mapping().unwrap();
1559 let empty_seq = empty_mapping.get_sequence("items").unwrap();
1560
1561 assert_eq!(empty_seq.len(), 0);
1562 assert!(empty_seq.is_empty());
1563 }
1564
1565 #[test]
1566 fn test_sequence_get() {
1567 use crate::Document;
1568 let doc = Document::from_str("items:\n - first\n - second\n - third").unwrap();
1569 let mapping = doc.as_mapping().unwrap();
1570 let seq = mapping.get_sequence("items").unwrap();
1571
1572 assert_eq!(seq.get(0).unwrap().to_string(), "first");
1573 assert_eq!(seq.get(1).unwrap().to_string(), "second");
1574 assert_eq!(seq.get(2).unwrap().to_string(), "third");
1575 assert!(seq.get(3).is_none());
1576 }
1577
1578 #[test]
1579 fn test_sequence_first_and_last() {
1580 use crate::Document;
1581 let doc = Document::from_str("items:\n - first\n - middle\n - last").unwrap();
1582 let mapping = doc.as_mapping().unwrap();
1583 let seq = mapping.get_sequence("items").unwrap();
1584
1585 assert_eq!(seq.first().unwrap().to_string(), "first");
1586 assert_eq!(seq.last().unwrap().to_string(), "last");
1587
1588 let empty_doc = Document::from_str("items: []").unwrap();
1589 let empty_mapping = empty_doc.as_mapping().unwrap();
1590 let empty_seq = empty_mapping.get_sequence("items").unwrap();
1591
1592 assert!(empty_seq.first().is_none());
1593 assert!(empty_seq.last().is_none());
1594 }
1595
1596 #[test]
1597 fn test_sequence_values_iterator() {
1598 use crate::Document;
1599 let doc = Document::from_str("items:\n - a\n - b\n - c").unwrap();
1600 let mapping = doc.as_mapping().unwrap();
1601 let seq = mapping.get_sequence("items").unwrap();
1602
1603 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1604 assert_eq!(values, vec!["a", "b", "c"]);
1605 }
1606
1607 #[test]
1608 fn test_sequence_pop() {
1609 use crate::Document;
1610 let doc = Document::from_str("items:\n - a\n - b\n - c").unwrap();
1611 let mapping = doc.as_mapping().unwrap();
1612 let seq = mapping.get_sequence("items").unwrap();
1613
1614 assert_eq!(seq.len(), 3);
1615 let popped = seq.pop().unwrap();
1616 assert_eq!(popped.to_string(), "c");
1617 assert_eq!(seq.len(), 2);
1618
1619 let popped = seq.pop().unwrap();
1620 assert_eq!(popped.to_string(), "b");
1621 assert_eq!(seq.len(), 1);
1622
1623 let expected = "items:\n - a";
1624 assert_eq!(doc.to_string().trim_end(), expected);
1625
1626 let popped = seq.pop().unwrap();
1627 assert_eq!(popped.to_string(), "a");
1628 assert_eq!(seq.len(), 0);
1629 assert!(seq.pop().is_none());
1630 }
1631
1632 #[test]
1633 fn test_sequence_clear() {
1634 use crate::Document;
1635 let doc = Document::from_str("items:\n - a\n - b\n - c").unwrap();
1636 let mapping = doc.as_mapping().unwrap();
1637 let seq = mapping.get_sequence("items").unwrap();
1638
1639 assert_eq!(seq.len(), 3);
1640 seq.clear();
1641 assert_eq!(seq.len(), 0);
1642 assert!(seq.is_empty());
1643 }
1644
1645 #[test]
1646 fn test_sequence_get_with_nested_values() {
1647 use crate::Document;
1648 let doc = Document::from_str(
1649 r#"items:
1650 - simple
1651 - {key: value}
1652 - [nested, list]"#,
1653 )
1654 .unwrap();
1655 let mapping = doc.as_mapping().unwrap();
1656 let seq = mapping.get_sequence("items").unwrap();
1657
1658 assert_eq!(seq.len(), 3);
1659 assert!(seq.get(0).unwrap().is_scalar());
1660 assert!(seq.get(1).unwrap().is_mapping());
1661 assert!(seq.get(2).unwrap().is_sequence());
1662 }
1663
1664 #[test]
1665 fn test_flow_sequence_len_and_is_empty() {
1666 use crate::Document;
1667 let doc = Document::from_str("items: [a, b, c]").unwrap();
1668 let mapping = doc.as_mapping().unwrap();
1669 let seq = mapping.get_sequence("items").unwrap();
1670
1671 assert_eq!(seq.len(), 3);
1672 assert!(!seq.is_empty());
1673
1674 let empty_doc = Document::from_str("items: []").unwrap();
1675 let empty_mapping = empty_doc.as_mapping().unwrap();
1676 let empty_seq = empty_mapping.get_sequence("items").unwrap();
1677
1678 assert_eq!(empty_seq.len(), 0);
1679 assert!(empty_seq.is_empty());
1680 }
1681
1682 #[test]
1683 fn test_flow_sequence_get() {
1684 use crate::Document;
1685 let doc = Document::from_str("items: [first, second, third]").unwrap();
1686 let mapping = doc.as_mapping().unwrap();
1687 let seq = mapping.get_sequence("items").unwrap();
1688
1689 assert_eq!(seq.get(0).unwrap().to_string(), "first");
1690 assert_eq!(seq.get(1).unwrap().to_string(), "second");
1691 assert_eq!(seq.get(2).unwrap().to_string(), "third");
1692 assert!(seq.get(3).is_none());
1693 }
1694
1695 #[test]
1696 fn test_flow_sequence_first_and_last() {
1697 use crate::Document;
1698 let doc = Document::from_str("items: [first, middle, last]").unwrap();
1699 let mapping = doc.as_mapping().unwrap();
1700 let seq = mapping.get_sequence("items").unwrap();
1701
1702 assert_eq!(seq.first().unwrap().to_string(), "first");
1703 assert_eq!(seq.last().unwrap().to_string(), "last");
1704 }
1705
1706 #[test]
1707 fn test_flow_sequence_values_iterator() {
1708 use crate::Document;
1709 let doc = Document::from_str("items: [a, b, c]").unwrap();
1710 let mapping = doc.as_mapping().unwrap();
1711 let seq = mapping.get_sequence("items").unwrap();
1712
1713 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1714 assert_eq!(values, vec!["a", "b", "c"]);
1715 }
1716
1717 #[test]
1718 fn test_flow_sequence_remove_middle() {
1719 use crate::Document;
1720 let doc = Document::from_str("items: [a, b, c]").unwrap();
1721 let mapping = doc.as_mapping().unwrap();
1722 let seq = mapping.get_sequence("items").unwrap();
1723
1724 assert_eq!(seq.len(), 3);
1725 let removed = seq.remove(1);
1726 assert_eq!(removed.map(|v| v.to_string()), Some("b".to_string()));
1727 assert_eq!(seq.len(), 2);
1728
1729 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1730 assert_eq!(values, vec!["a", "c"]);
1731 }
1732
1733 #[test]
1734 fn test_flow_sequence_remove_first() {
1735 use crate::Document;
1736 let doc = Document::from_str("items: [a, b, c]").unwrap();
1737 let mapping = doc.as_mapping().unwrap();
1738 let seq = mapping.get_sequence("items").unwrap();
1739
1740 assert_eq!(seq.len(), 3);
1741 let removed = seq.remove(0);
1742 assert_eq!(removed.map(|v| v.to_string()), Some("a".to_string()));
1743 assert_eq!(seq.len(), 2);
1744
1745 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1746 assert_eq!(values, vec!["b", "c"]);
1747 }
1748
1749 #[test]
1750 fn test_flow_sequence_remove_last() {
1751 use crate::Document;
1752 let doc = Document::from_str("items: [a, b, c]").unwrap();
1753 let mapping = doc.as_mapping().unwrap();
1754 let seq = mapping.get_sequence("items").unwrap();
1755
1756 assert_eq!(seq.len(), 3);
1757 let removed = seq.remove(2);
1758 assert_eq!(removed.map(|v| v.to_string()), Some("c".to_string()));
1759 assert_eq!(seq.len(), 2);
1760
1761 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1762 assert_eq!(values, vec!["a", "b"]);
1763 }
1764
1765 #[test]
1766 fn test_flow_sequence_pop() {
1767 use crate::Document;
1768 let doc = Document::from_str("items: [a, b, c]").unwrap();
1769 let mapping = doc.as_mapping().unwrap();
1770 let seq = mapping.get_sequence("items").unwrap();
1771
1772 assert_eq!(seq.len(), 3);
1773 let popped = seq.pop().unwrap();
1774 assert_eq!(popped.to_string(), "c");
1775 assert_eq!(seq.len(), 2);
1776
1777 let popped = seq.pop().unwrap();
1778 assert_eq!(popped.to_string(), "b");
1779 assert_eq!(seq.len(), 1);
1780
1781 let popped = seq.pop().unwrap();
1782 assert_eq!(popped.to_string(), "a");
1783 assert_eq!(seq.len(), 0);
1784 assert!(seq.pop().is_none());
1785 }
1786
1787 #[test]
1788 fn test_flow_sequence_clear() {
1789 use crate::Document;
1790 let doc = Document::from_str("items: [a, b, c]").unwrap();
1791 let mapping = doc.as_mapping().unwrap();
1792 let seq = mapping.get_sequence("items").unwrap();
1793
1794 assert_eq!(seq.len(), 3);
1795 seq.clear();
1796 assert_eq!(seq.len(), 0);
1797 assert!(seq.is_empty());
1798 }
1799
1800 #[test]
1801 fn test_flow_sequence_with_whitespace() {
1802 use crate::Document;
1803 let doc = Document::from_str("items: [ a , b , c ]").unwrap();
1804 let mapping = doc.as_mapping().unwrap();
1805 let seq = mapping.get_sequence("items").unwrap();
1806
1807 assert_eq!(seq.len(), 3);
1808 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1809 assert_eq!(values, vec!["a", "b", "c"]);
1810 }
1811
1812 #[test]
1813 fn test_block_sequence_remove_middle() {
1814 use crate::Document;
1815 let doc = Document::from_str("items:\n - a\n - b\n - c").unwrap();
1816 let mapping = doc.as_mapping().unwrap();
1817 let seq = mapping.get_sequence("items").unwrap();
1818
1819 assert_eq!(seq.len(), 3);
1820 let removed = seq.remove(1);
1821 assert_eq!(removed.map(|v| v.to_string()), Some("b".to_string()));
1822 assert_eq!(seq.len(), 2);
1823
1824 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1825 assert_eq!(values, vec!["a", "c"]);
1826 }
1827
1828 #[test]
1829 fn test_block_sequence_remove_first() {
1830 use crate::Document;
1831 let doc = Document::from_str("items:\n - a\n - b\n - c").unwrap();
1832 let mapping = doc.as_mapping().unwrap();
1833 let seq = mapping.get_sequence("items").unwrap();
1834
1835 assert_eq!(seq.len(), 3);
1836 let removed = seq.remove(0);
1837 assert_eq!(removed.map(|v| v.to_string()), Some("a".to_string()));
1838 assert_eq!(seq.len(), 2);
1839
1840 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1841 assert_eq!(values, vec!["b", "c"]);
1842 }
1843
1844 #[test]
1845 fn test_block_sequence_remove_last() {
1846 use crate::Document;
1847 let doc = Document::from_str("items:\n - a\n - b\n - c").unwrap();
1848 let mapping = doc.as_mapping().unwrap();
1849 let seq = mapping.get_sequence("items").unwrap();
1850
1851 assert_eq!(seq.len(), 3);
1852 let removed = seq.remove(2);
1853 assert_eq!(removed.map(|v| v.to_string()), Some("c".to_string()));
1854 assert_eq!(seq.len(), 2);
1855
1856 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1857 assert_eq!(values, vec!["a", "b"]);
1858 }
1859
1860 #[test]
1861 fn test_single_item_block_sequence_remove() {
1862 use crate::Document;
1863 let doc = Document::from_str("items:\n - only").unwrap();
1864 let mapping = doc.as_mapping().unwrap();
1865 let seq = mapping.get_sequence("items").unwrap();
1866
1867 assert_eq!(seq.len(), 1);
1868 let removed = seq.remove(0);
1869 assert_eq!(removed.map(|v| v.to_string()), Some("only".to_string()));
1870 assert_eq!(seq.len(), 0);
1871 }
1872
1873 #[test]
1874 fn test_single_item_flow_sequence_remove() {
1875 use crate::Document;
1876 let doc = Document::from_str("items: [only]").unwrap();
1877 let mapping = doc.as_mapping().unwrap();
1878 let seq = mapping.get_sequence("items").unwrap();
1879
1880 assert_eq!(seq.len(), 1);
1881 let removed = seq.remove(0);
1882 assert_eq!(removed.map(|v| v.to_string()), Some("only".to_string()));
1883 assert_eq!(seq.len(), 0);
1884 }
1885
1886 #[test]
1887 fn test_flow_sequence_push() {
1888 use crate::Document;
1889 let doc = Document::from_str("items: [a, b]").unwrap();
1890 let mapping = doc.as_mapping().unwrap();
1891 let seq = mapping.get_sequence("items").unwrap();
1892
1893 assert_eq!(seq.len(), 2);
1894 seq.push("c");
1895 assert_eq!(seq.len(), 3);
1896
1897 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1898 assert_eq!(values, vec!["a", "b", "c"]);
1899 }
1900
1901 #[test]
1902 fn test_flow_sequence_push_multiple() {
1903 use crate::Document;
1904 let doc = Document::from_str("items: [a]").unwrap();
1905 let mapping = doc.as_mapping().unwrap();
1906 let seq = mapping.get_sequence("items").unwrap();
1907
1908 seq.push("b");
1909 seq.push("c");
1910 seq.push("d");
1911
1912 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1913 assert_eq!(values, vec!["a", "b", "c", "d"]);
1914 }
1915
1916 #[test]
1917 fn test_flow_sequence_set_item() {
1918 use crate::Document;
1919 let doc = Document::from_str("items: [a, b, c]").unwrap();
1920 let mapping = doc.as_mapping().unwrap();
1921 let seq = mapping.get_sequence("items").unwrap();
1922
1923 seq.set(1, "modified");
1924
1925 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1926 assert_eq!(values, vec!["a", "modified", "c"]);
1927 }
1928
1929 #[test]
1930 fn test_flow_sequence_insert_beginning() {
1931 use crate::Document;
1932 let doc = Document::from_str("items: [b, c]").unwrap();
1933 let mapping = doc.as_mapping().unwrap();
1934 let seq = mapping.get_sequence("items").unwrap();
1935
1936 seq.insert(0, "a");
1937
1938 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1939 assert_eq!(values, vec!["a", "b", "c"]);
1940 }
1941
1942 #[test]
1943 fn test_flow_sequence_insert_middle() {
1944 use crate::Document;
1945 let doc = Document::from_str("items: [a, c]").unwrap();
1946 let mapping = doc.as_mapping().unwrap();
1947 let seq = mapping.get_sequence("items").unwrap();
1948
1949 seq.insert(1, "b");
1950
1951 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1952 assert_eq!(values, vec!["a", "b", "c"]);
1953 }
1954
1955 #[test]
1956 fn test_flow_sequence_insert_end() {
1957 use crate::Document;
1958 let doc = Document::from_str("items: [a, b]").unwrap();
1959 let mapping = doc.as_mapping().unwrap();
1960 let seq = mapping.get_sequence("items").unwrap();
1961
1962 seq.insert(2, "c");
1963
1964 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1965 assert_eq!(values, vec!["a", "b", "c"]);
1966 }
1967
1968 #[test]
1969 fn test_block_sequence_push() {
1970 use crate::Document;
1971 let doc = Document::from_str("items:\n - a\n - b").unwrap();
1972 let mapping = doc.as_mapping().unwrap();
1973 let seq = mapping.get_sequence("items").unwrap();
1974
1975 assert_eq!(seq.len(), 2);
1976 seq.push("c");
1977 assert_eq!(seq.len(), 3);
1978
1979 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1980 assert_eq!(values, vec!["a", "b", "c"]);
1981 }
1982
1983 #[test]
1984 fn test_block_sequence_set_item() {
1985 use crate::Document;
1986 let doc = Document::from_str("items:\n - a\n - b\n - c").unwrap();
1987 let mapping = doc.as_mapping().unwrap();
1988 let seq = mapping.get_sequence("items").unwrap();
1989
1990 seq.set(1, "modified");
1991
1992 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
1993 assert_eq!(values, vec!["a", "modified", "c"]);
1994 }
1995
1996 #[test]
1997 fn test_block_sequence_insert_beginning() {
1998 use crate::Document;
1999 let doc = Document::from_str("items:\n - b\n - c").unwrap();
2000 let mapping = doc.as_mapping().unwrap();
2001 let seq = mapping.get_sequence("items").unwrap();
2002
2003 seq.insert(0, "a");
2004
2005 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
2006 assert_eq!(values, vec!["a", "b", "c"]);
2007 }
2008
2009 #[test]
2010 fn test_block_sequence_insert_middle() {
2011 use crate::Document;
2012 let doc = Document::from_str("items:\n - a\n - c").unwrap();
2013 let mapping = doc.as_mapping().unwrap();
2014 let seq = mapping.get_sequence("items").unwrap();
2015
2016 seq.insert(1, "b");
2017
2018 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
2019 assert_eq!(values, vec!["a", "b", "c"]);
2020 }
2021
2022 #[test]
2023 fn test_block_sequence_insert_end() {
2024 use crate::Document;
2025 let doc = Document::from_str("items:\n - a\n - b").unwrap();
2026 let mapping = doc.as_mapping().unwrap();
2027 let seq = mapping.get_sequence("items").unwrap();
2028
2029 seq.insert(2, "c");
2030
2031 let values: Vec<String> = seq.values().map(|v| v.to_string()).collect();
2032 assert_eq!(values, vec!["a", "b", "c"]);
2033 }
2034
2035 #[test]
2036 fn test_sequence_get_node() {
2037 let doc = YamlFile::from_str("items:\n - alpha\n - beta\n - gamma")
2038 .unwrap()
2039 .document()
2040 .unwrap();
2041 let seq = doc.as_mapping().unwrap().get_sequence("items").unwrap();
2042
2043 assert_eq!(seq.len(), 3);
2044 assert!(seq.get(0).is_some());
2045 assert!(seq.get(1).is_some());
2046 assert!(seq.get(2).is_some());
2047 assert!(seq.get(3).is_none());
2048
2049 assert_eq!(
2050 seq.get(0).unwrap().as_scalar().unwrap().as_string(),
2051 "alpha"
2052 );
2053 assert_eq!(seq.get(1).unwrap().as_scalar().unwrap().as_string(), "beta");
2054 assert_eq!(
2055 seq.get(2).unwrap().as_scalar().unwrap().as_string(),
2056 "gamma"
2057 );
2058 }
2059
2060 #[test]
2061 fn test_sequence_set_with_nested_mapping() {
2062 use crate::path::YamlPath;
2063 use crate::Document;
2064
2065 let yaml_str = "items:\n - name: first\n value: 1\n - name: second\n value: 2\n";
2066 let doc = Document::from_str(yaml_str).unwrap();
2067
2068 let items_node = doc.try_get_path("items").unwrap();
2069 let items = items_node.as_sequence().unwrap();
2070
2071 assert_eq!(items.len(), 2);
2072 let first = items.get(0).unwrap();
2073 assert!(first.is_mapping());
2074
2075 items.set(0, "replaced");
2076 assert_eq!(
2077 doc.to_string(),
2078 "items:\n - replaced\n - name: second\n value: 2\n"
2079 );
2080 }
2081}