1use crate::{
4 lex::{lex, SyntaxKind},
5 parse::Parse,
6 scalar::ScalarValue,
7 value::YamlValue,
8 PositionedParseError,
9};
10use rowan::ast::AstNode;
11use rowan::{GreenNodeBuilder, TextRange};
12use std::path::Path;
13use std::str::FromStr;
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct ParsedYaml {
18 pub green_node: rowan::GreenNode,
19 pub errors: Vec<String>,
20 pub positioned_errors: Vec<PositionedParseError>,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub enum Lang {}
26
27impl rowan::Language for Lang {
28 type Kind = SyntaxKind;
29
30 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
31 unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
32 }
33
34 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
35 kind.into()
36 }
37}
38
39type SyntaxNode = rowan::SyntaxNode<Lang>;
40type SyntaxToken = rowan::SyntaxToken<Lang>;
41
42macro_rules! ast_node {
44 ($ast:ident, $kind:ident, $doc:expr) => {
45 #[doc = $doc]
46 #[derive(PartialEq, Eq, Hash)]
47 pub struct $ast(SyntaxNode);
48
49 impl std::fmt::Debug for $ast {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 f.debug_struct(stringify!($ast))
52 .field("syntax", &self.0)
53 .finish()
54 }
55 }
56
57 impl AstNode for $ast {
58 type Language = Lang;
59
60 fn can_cast(kind: SyntaxKind) -> bool {
61 kind == SyntaxKind::$kind
62 }
63
64 fn cast(syntax: SyntaxNode) -> Option<Self> {
65 if Self::can_cast(syntax.kind()) {
66 Some(Self(syntax))
67 } else {
68 None
69 }
70 }
71
72 fn syntax(&self) -> &SyntaxNode {
73 &self.0
74 }
75 }
76
77 impl From<SyntaxNode> for $ast {
78 fn from(node: SyntaxNode) -> Self {
79 $ast(node)
80 }
81 }
82
83 impl std::fmt::Display for $ast {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 write!(f, "{}", self.0.text())
86 }
87 }
88 };
89}
90
91ast_node!(
92 Yaml,
93 ROOT,
94 "A YAML document containing one or more documents"
95);
96ast_node!(Document, DOCUMENT, "A single YAML document");
97ast_node!(Sequence, SEQUENCE, "A YAML sequence (list)");
98ast_node!(Mapping, MAPPING, "A YAML mapping (key-value pairs)");
99ast_node!(Scalar, SCALAR, "A YAML scalar value");
100
101impl Default for Yaml {
102 fn default() -> Self {
103 Self::new()
104 }
105}
106
107impl Yaml {
108 pub fn new() -> Yaml {
110 let mut builder = GreenNodeBuilder::new();
111 builder.start_node(SyntaxKind::ROOT.into());
112 builder.finish_node();
113 Yaml(SyntaxNode::new_root_mut(builder.finish()))
114 }
115
116 pub fn parse(text: &str) -> Parse<Yaml> {
118 Parse::parse_yaml(text)
119 }
120
121 pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Yaml, crate::Error> {
123 let contents = std::fs::read_to_string(path)?;
124 Ok(Self::from_str(&contents)?)
125 }
126
127 pub fn documents(&self) -> impl Iterator<Item = Document> {
129 self.0.children().filter_map(Document::cast)
130 }
131
132 pub fn document(&self) -> Option<Document> {
134 self.documents().next()
135 }
136
137 pub fn push_document(&mut self, document: Document) {
139 let children_count = self.0.children_with_tokens().count();
140 let mut new_nodes = Vec::new();
141
142 if self.0.children().count() > 0 {
144 new_nodes.push(create_token_green(SyntaxKind::NEWLINE, "\n").into());
145 new_nodes.push(create_token_green(SyntaxKind::DOC_START, "---").into());
146 new_nodes.push(create_token_green(SyntaxKind::NEWLINE, "\n").into());
147 }
148
149 new_nodes.push(document.0.green().into());
151
152 let new_green = self
154 .0
155 .green()
156 .splice_children(children_count..children_count, new_nodes);
157 self.0 = SyntaxNode::new_root_mut(new_green);
158 }
159}
160
161impl Document {
162 pub fn new() -> Document {
164 let mut builder = GreenNodeBuilder::new();
165 builder.start_node(SyntaxKind::DOCUMENT.into());
166 builder.finish_node();
167 Document(SyntaxNode::new_root_mut(builder.finish()))
168 }
169
170 pub fn root_node(&self) -> Option<SyntaxNode> {
172 self.0.children().find(|child| {
173 matches!(
174 child.kind(),
175 SyntaxKind::MAPPING | SyntaxKind::SEQUENCE | SyntaxKind::SCALAR
176 )
177 })
178 }
179
180 pub fn as_mapping(&self) -> Option<Mapping> {
182 self.root_node().and_then(Mapping::cast)
183 }
184
185 pub fn as_sequence(&self) -> Option<Sequence> {
187 self.root_node().and_then(Sequence::cast)
188 }
189
190 pub fn as_scalar(&self) -> Option<Scalar> {
192 self.root_node().and_then(Scalar::cast)
193 }
194}
195
196fn is_scalar_token(kind: SyntaxKind) -> bool {
198 matches!(
199 kind,
200 SyntaxKind::STRING
201 | SyntaxKind::INT
202 | SyntaxKind::FLOAT
203 | SyntaxKind::BOOL
204 | SyntaxKind::NULL
205 | SyntaxKind::VALUE
206 )
207}
208
209impl Mapping {
210 pub fn pairs(&self) -> impl Iterator<Item = (Option<Scalar>, Option<SyntaxNode>)> {
212 let children: Vec<_> = self.0.children().collect();
214 let mut pairs = Vec::new();
215 let mut i = 0;
216
217 while i < children.len() {
218 if children[i].kind() == SyntaxKind::KEY {
220 let key_text = children[i].text().to_string();
221 let key_scalar = Scalar(create_scalar_node(&key_text));
222
223 let mut value_node = None;
225 let mut j = i + 1;
226 while j < children.len() {
227 if children[j].kind() == SyntaxKind::VALUE {
228 value_node = Some(children[j].clone());
229 break;
230 }
231 j += 1;
232 }
233
234 pairs.push((Some(key_scalar), value_node));
235 i = j + 1;
236 } else {
237 i += 1;
238 }
239 }
240
241 pairs.into_iter()
242 }
243
244 pub fn get(&self, key: &str) -> Option<SyntaxNode> {
246 self.pairs()
247 .find(|(k, _)| k.as_ref().map(|s| s.value()) == Some(key.to_string()))
248 .and_then(|(_, v)| v)
249 }
250}
251
252impl Sequence {
253 pub fn items(&self) -> impl Iterator<Item = SyntaxNode> {
255 std::iter::empty()
257 }
258}
259
260impl Scalar {
261 pub fn value(&self) -> String {
263 self.0.text().to_string()
264 }
265
266 pub fn is_quoted(&self) -> bool {
268 let text = self.value();
269 (text.starts_with('"') && text.ends_with('"'))
270 || (text.starts_with('\'') && text.ends_with('\''))
271 }
272
273 pub fn unquoted_value(&self) -> String {
275 let text = self.value();
276 if self.is_quoted() {
277 text[1..text.len() - 1].to_string()
279 } else {
280 text.to_string()
281 }
282 }
283}
284
285impl FromStr for Yaml {
286 type Err = crate::ParseError;
287
288 fn from_str(s: &str) -> Result<Self, Self::Err> {
289 Yaml::parse(s).to_result()
290 }
291}
292
293struct Parser {
295 tokens: Vec<(SyntaxKind, String)>,
296 current_token_index: usize,
297 builder: GreenNodeBuilder<'static>,
298 errors: Vec<String>,
299 positioned_errors: Vec<PositionedParseError>,
300 token_positions: Vec<(SyntaxKind, rowan::TextSize, rowan::TextSize)>,
301 in_flow_context: bool,
302}
303
304impl Parser {
305 fn new(text: &str) -> Self {
306 let lexed = lex(text);
307 let mut tokens = Vec::new();
308 let mut token_positions = Vec::new();
309 let mut offset = rowan::TextSize::from(0);
310
311 for (kind, token_text) in lexed {
312 let start = offset;
313 let len = rowan::TextSize::from(token_text.len() as u32);
314 let end = start + len;
315
316 tokens.push((kind, token_text.to_string()));
317 token_positions.push((kind, start, end));
318 offset = end;
319 }
320
321 let token_count = tokens.len();
323 tokens.reverse();
324
325 Self {
326 tokens,
327 current_token_index: token_count,
328 builder: GreenNodeBuilder::new(),
329 errors: Vec::new(),
330 positioned_errors: Vec::new(),
331 token_positions,
332 in_flow_context: false,
333 }
334 }
335
336 fn parse(mut self) -> ParsedYaml {
337 self.builder.start_node(SyntaxKind::ROOT.into());
338
339 self.skip_ws_and_newlines();
340
341 if self.current().is_some() {
344 self.parse_document();
345 self.skip_ws_and_newlines();
346
347 while self.current() == Some(SyntaxKind::DOC_START) {
349 self.parse_document();
350 self.skip_ws_and_newlines();
351 }
352 }
353
354 self.builder.finish_node();
355
356 ParsedYaml {
357 green_node: self.builder.finish(),
358 errors: self.errors,
359 positioned_errors: self.positioned_errors,
360 }
361 }
362
363 fn parse_document(&mut self) {
364 self.builder.start_node(SyntaxKind::DOCUMENT.into());
365
366 if self.current() == Some(SyntaxKind::DOC_START) {
368 self.bump();
369 self.skip_ws_and_newlines();
370 }
371
372 if self.current().is_some()
374 && self.current() != Some(SyntaxKind::DOC_END)
375 && self.current() != Some(SyntaxKind::DOC_START)
376 {
377 self.parse_value();
378 }
379
380 if self.current() == Some(SyntaxKind::DOC_END) {
382 self.bump();
383 }
384
385 self.builder.finish_node();
386 }
387
388 fn parse_value(&mut self) {
389 match self.current() {
390 Some(SyntaxKind::DASH) if !self.in_flow_context => self.parse_sequence(),
391 Some(
392 SyntaxKind::STRING
393 | SyntaxKind::INT
394 | SyntaxKind::FLOAT
395 | SyntaxKind::BOOL
396 | SyntaxKind::NULL,
397 ) => {
398 if !self.in_flow_context && self.is_mapping_key() {
401 self.parse_mapping();
402 } else {
403 self.parse_scalar();
404 }
405 }
406 Some(SyntaxKind::LEFT_BRACKET) => self.parse_flow_sequence(),
407 Some(SyntaxKind::LEFT_BRACE) => self.parse_flow_mapping(),
408 _ => self.parse_scalar(),
409 }
410 }
411
412 fn parse_scalar(&mut self) {
413 self.builder.start_node(SyntaxKind::SCALAR.into());
414
415 if matches!(
417 self.current(),
418 Some(SyntaxKind::QUOTE | SyntaxKind::SINGLE_QUOTE)
419 ) {
420 let quote_type = self.current().unwrap();
421 self.bump(); while self.current().is_some() && self.current() != Some(quote_type) {
425 self.bump();
426 }
427
428 if self.current() == Some(quote_type) {
429 self.bump(); } else {
431 self.add_error("Unterminated quoted string".to_string());
432 }
433 } else {
434 if matches!(
436 self.current(),
437 Some(
438 SyntaxKind::STRING
439 | SyntaxKind::INT
440 | SyntaxKind::FLOAT
441 | SyntaxKind::BOOL
442 | SyntaxKind::NULL
443 )
444 ) {
445 self.bump();
446 } else {
447 while let Some(kind) = self.current() {
449 if matches!(
450 kind,
451 SyntaxKind::NEWLINE
452 | SyntaxKind::COLON
453 | SyntaxKind::DASH
454 | SyntaxKind::COMMENT
455 | SyntaxKind::DOC_START
456 | SyntaxKind::DOC_END
457 ) {
458 break;
459 }
460 self.bump();
461 }
462 }
463 }
464
465 self.builder.finish_node();
466 }
467
468 fn parse_mapping(&mut self) {
469 self.builder.start_node(SyntaxKind::MAPPING.into());
470
471 while self.current().is_some() {
472 if !self.is_mapping_key() {
473 break;
474 }
475
476 self.builder.start_node(SyntaxKind::KEY.into());
478 if matches!(
479 self.current(),
480 Some(
481 SyntaxKind::STRING
482 | SyntaxKind::INT
483 | SyntaxKind::FLOAT
484 | SyntaxKind::BOOL
485 | SyntaxKind::NULL
486 )
487 ) {
488 self.bump(); }
490 self.builder.finish_node();
491
492 self.skip_whitespace();
493
494 if self.current() == Some(SyntaxKind::COLON) {
496 self.bump();
497 self.skip_whitespace();
498
499 self.builder.start_node(SyntaxKind::VALUE.into());
501 if self.current().is_some() && self.current() != Some(SyntaxKind::NEWLINE) {
502 self.parse_value();
503 } else if self.current() == Some(SyntaxKind::NEWLINE) {
504 self.bump(); if self.current() == Some(SyntaxKind::INDENT) {
507 self.bump(); self.parse_value();
510 }
511 }
512 self.builder.finish_node();
514 } else {
515 self.add_error("Expected ':' after mapping key".to_string());
516 }
517
518 self.skip_ws_and_newlines();
519 }
520
521 self.builder.finish_node();
522 }
523
524 fn parse_sequence(&mut self) {
525 self.builder.start_node(SyntaxKind::SEQUENCE.into());
526
527 while self.current() == Some(SyntaxKind::DASH) {
528 self.bump(); self.skip_whitespace();
530
531 if self.current().is_some() && self.current() != Some(SyntaxKind::NEWLINE) {
532 self.parse_value();
533 }
534
535 self.skip_ws_and_newlines();
536 }
537
538 self.builder.finish_node();
539 }
540
541 fn parse_flow_sequence(&mut self) {
542 self.builder.start_node(SyntaxKind::SEQUENCE.into());
543
544 self.bump(); self.skip_whitespace();
546
547 let prev_flow = self.in_flow_context;
548 self.in_flow_context = true;
549
550 while self.current() != Some(SyntaxKind::RIGHT_BRACKET) && self.current().is_some() {
551 self.parse_value();
552 self.skip_whitespace();
553
554 if self.current() == Some(SyntaxKind::COMMA) {
555 self.bump();
556 self.skip_whitespace();
557 }
558 }
559
560 self.in_flow_context = prev_flow;
561
562 if self.current() == Some(SyntaxKind::RIGHT_BRACKET) {
563 self.bump();
564 } else {
565 self.add_error("Expected ']' to close flow sequence".to_string());
566 }
567
568 self.builder.finish_node();
569 }
570
571 fn parse_flow_mapping(&mut self) {
572 self.builder.start_node(SyntaxKind::MAPPING.into());
573
574 self.bump(); self.skip_whitespace();
576
577 let prev_flow = self.in_flow_context;
578 self.in_flow_context = true;
579
580 while self.current() != Some(SyntaxKind::RIGHT_BRACE) && self.current().is_some() {
581 self.parse_value();
583 self.skip_whitespace();
584
585 if self.current() == Some(SyntaxKind::COLON) {
586 self.bump();
587 self.skip_whitespace();
588 self.parse_value();
589 } else {
590 self.add_error("Expected ':' in flow mapping".to_string());
591 }
592
593 self.skip_whitespace();
594
595 if self.current() == Some(SyntaxKind::COMMA) {
596 self.bump();
597 self.skip_whitespace();
598 }
599 }
600
601 self.in_flow_context = prev_flow;
602
603 if self.current() == Some(SyntaxKind::RIGHT_BRACE) {
604 self.bump();
605 } else {
606 self.add_error("Expected '}' to close flow mapping".to_string());
607 }
608
609 self.builder.finish_node();
610 }
611
612 fn is_mapping_key(&self) -> bool {
613 for kind in self.upcoming_tokens() {
615 match kind {
616 SyntaxKind::COLON => return true,
617 SyntaxKind::WHITESPACE => continue,
618 SyntaxKind::NEWLINE
620 | SyntaxKind::DASH
621 | SyntaxKind::DOC_START
622 | SyntaxKind::DOC_END
623 | SyntaxKind::COMMA
624 | SyntaxKind::RIGHT_BRACKET
625 | SyntaxKind::RIGHT_BRACE => return false,
626 _ => continue,
628 }
629 }
630 false
631 }
632
633 fn skip_whitespace(&mut self) {
634 while self.current() == Some(SyntaxKind::WHITESPACE) {
635 self.bump();
636 }
637 }
638
639 fn skip_ws_and_newlines(&mut self) {
640 while matches!(
641 self.current(),
642 Some(
643 SyntaxKind::WHITESPACE
644 | SyntaxKind::NEWLINE
645 | SyntaxKind::INDENT
646 | SyntaxKind::COMMENT
647 )
648 ) {
649 self.bump();
650 }
651 }
652
653 fn bump(&mut self) {
654 if let Some((kind, text)) = self.tokens.pop() {
655 self.builder.token(kind.into(), &text);
656 if self.current_token_index > 0 {
657 self.current_token_index -= 1;
658 }
659 }
660 }
661
662 fn current(&self) -> Option<SyntaxKind> {
663 self.tokens.last().map(|(kind, _)| *kind)
664 }
665
666 fn nth(&self, n: usize) -> Option<SyntaxKind> {
668 if n >= self.tokens.len() {
669 None
670 } else {
671 let idx = self.tokens.len() - 1 - n;
672 Some(self.tokens[idx].0)
673 }
674 }
675
676 fn upcoming_tokens(&self) -> impl Iterator<Item = SyntaxKind> + '_ {
678 (1..self.tokens.len()).map(move |i| {
679 let idx = self.tokens.len() - 1 - i;
680 self.tokens[idx].0
681 })
682 }
683
684 fn add_error(&mut self, message: String) {
685 self.errors.push(message);
686 }
687}
688
689pub fn parse(text: &str) -> ParsedYaml {
691 let parser = Parser::new(text);
692 parser.parse()
693}
694
695#[cfg(test)]
696mod tests {
697 use super::*;
698
699 #[test]
700 fn test_simple_mapping() {
701 let yaml = "key: value";
702 let parsed = Yaml::from_str(yaml).unwrap();
703 let doc = parsed.document().unwrap();
704 let mapping = doc.as_mapping().unwrap();
705
706 assert_eq!(parsed.to_string().trim(), "key: value");
708
709 let value = mapping.get("key");
711 assert!(value.is_some());
712 }
713
714 #[test]
715 fn test_simple_sequence() {
716 let yaml = "- item1\n- item2";
717 let parsed = Yaml::from_str(yaml);
718 assert!(parsed.is_ok());
719 }
720
721 #[test]
722 fn test_complex_yaml() {
723 let yaml = r#"
724name: my-app
725version: 1.0.0
726dependencies:
727 - serde
728 - tokio
729config:
730 port: 8080
731 enabled: true
732"#;
733 let parsed = Yaml::from_str(yaml).unwrap();
734 assert_eq!(parsed.documents().count(), 1);
735
736 let doc = parsed.document().unwrap();
737 assert!(doc.as_mapping().is_some());
738 }
739
740 #[test]
741 fn test_multiple_documents() {
742 let yaml = r#"---
743doc: first
744---
745doc: second
746...
747"#;
748 let parsed = Yaml::from_str(yaml).unwrap();
749 assert_eq!(parsed.documents().count(), 2);
750 }
751
752 #[test]
753 fn test_flow_styles() {
754 let yaml = r#"
755array: [1, 2, 3]
756object: {key: value, another: 42}
757"#;
758 let parsed = Yaml::from_str(yaml).unwrap();
759 assert!(parsed.document().is_some());
760 }
761
762 #[test]
763 fn test_scalar_types_parsing() {
764 let yaml = r#"
765string: hello
766integer: 42
767float: 3.14
768bool_true: true
769bool_false: false
770null_value: null
771tilde: ~
772"#;
773 let parsed = Yaml::from_str(yaml).unwrap();
774 let doc = parsed.document().unwrap();
775 let mapping = doc.as_mapping().unwrap();
776
777 assert!(mapping.get("string").is_some());
779 assert!(mapping.get("integer").is_some());
780 assert!(mapping.get("float").is_some());
781 assert!(mapping.get("bool_true").is_some());
782 assert!(mapping.get("bool_false").is_some());
783 assert!(mapping.get("null_value").is_some());
784 assert!(mapping.get("tilde").is_some());
785 }
786
787 #[test]
788 fn test_preserve_formatting() {
789 let yaml = r#"# Comment at start
790key: value # inline comment
791
792# Another comment
793list:
794 - item1
795 - item2
796"#;
797 let parsed = Yaml::from_str(yaml).unwrap();
798 let output = parsed.to_string();
799
800 assert!(output.contains("# Comment at start"));
802 assert!(output.contains("# inline comment"));
803 assert!(output.contains("# Another comment"));
804
805 assert!(output.contains("key: value"));
807 }
808
809 #[test]
810 fn test_quoted_strings() {
811 let yaml = r#"
812single: 'single quoted'
813double: "double quoted"
814plain: unquoted
815"#;
816 let parsed = Yaml::from_str(yaml).unwrap();
817 let doc = parsed.document().unwrap();
818 let mapping = doc.as_mapping().unwrap();
819
820 assert!(mapping.get("single").is_some());
821 assert!(mapping.get("double").is_some());
822 assert!(mapping.get("plain").is_some());
823 }
824
825 #[test]
826 fn test_nested_structures() {
827 let yaml = r#"
828root:
829 nested:
830 deeply:
831 value: 42
832 list:
833 - item1
834 - item2
835"#;
836 let parsed = Yaml::from_str(yaml).unwrap();
837 assert!(parsed.document().is_some());
838 }
839
840 #[test]
841 fn test_empty_values() {
842 let yaml = r#"
843empty_string: ""
844empty_after_colon:
845another_key: value
846"#;
847 let parsed = Yaml::from_str(yaml).unwrap();
848 let doc = parsed.document().unwrap();
849 let mapping = doc.as_mapping().unwrap();
850
851 assert!(mapping.get("empty_string").is_some());
852 assert!(mapping.get("another_key").is_some());
853 }
854
855 #[test]
856 fn test_special_characters() {
857 let yaml = r#"
858special: "line1\nline2"
859unicode: "emoji 😀"
860escaped: 'it\'s escaped'
861"#;
862 let result = Yaml::from_str(yaml);
863 assert!(result.is_ok());
865 }
866
867 #[test]
869 fn test_mapping_set_new_key() {
870 let yaml = "existing: value";
871 let mut parsed = Yaml::from_str(yaml).unwrap();
872
873 if let Some(doc) = parsed.document() {
874 if let Some(mut mapping) = doc.as_mapping() {
875 mapping.set("new_key", "new_value");
876 let output = parsed.to_string();
877 assert!(output.contains("new_key"));
878 assert!(output.contains("new_value"));
879 }
880 }
881 }
882
883 #[test]
884 fn test_mapping_rename_key() {
885 let yaml = "old_name: value";
886 let mut parsed = Yaml::from_str(yaml).unwrap();
887
888 if let Some(doc) = parsed.document() {
889 if let Some(mut mapping) = doc.as_mapping() {
890 let renamed = mapping.rename_key("old_name", "new_name");
891 assert!(renamed);
892 let output = parsed.to_string();
893 assert!(output.contains("new_name"));
894 assert!(!output.contains("old_name"));
895 }
896 }
897 }
898
899 #[test]
900 fn test_mapping_remove_key() {
901 let yaml = "key1: value1\nkey2: value2";
902 let mut parsed = Yaml::from_str(yaml).unwrap();
903
904 if let Some(doc) = parsed.document() {
905 if let Some(mut mapping) = doc.as_mapping() {
906 let removed = mapping.remove("key1");
907 assert!(removed);
908 let output = parsed.to_string();
909 assert!(!output.contains("key1"));
910 assert!(output.contains("key2"));
911 }
912 }
913 }
914
915 #[test]
916 fn test_sequence_operations() {
917 let yaml = "- item1\n- item2";
918 let mut parsed = Yaml::from_str(yaml).unwrap();
919
920 if let Some(doc) = parsed.document() {
921 if let Some(mut seq) = doc.as_sequence() {
922 seq.push("item3");
924 let output = parsed.to_string();
925 assert!(output.contains("item3"));
926
927 seq.insert(0, "item0");
929 let output = parsed.to_string();
930 assert!(output.contains("item0"));
931 }
932 }
933 }
934
935 #[test]
936 fn test_error_handling() {
937 let yaml = "key: value\n invalid indentation for key";
939 let result = Yaml::from_str(yaml);
940 let _ = result;
942 }
943}
944
945fn create_scalar_green(value: &str) -> rowan::GreenNode {
949 let mut builder = GreenNodeBuilder::new();
950 builder.start_node(SyntaxKind::SCALAR.into());
951 builder.token(SyntaxKind::VALUE.into(), value);
952 builder.finish_node();
953 builder.finish()
954}
955
956fn create_token_green(kind: SyntaxKind, text: &str) -> rowan::GreenToken {
957 rowan::GreenToken::new(kind.into(), text)
958}
959
960fn create_mapping_entry_green(
961 key: &str,
962 value: &str,
963) -> Vec<rowan::NodeOrToken<rowan::GreenNode, rowan::GreenToken>> {
964 vec![
965 create_scalar_green(key).into(),
966 create_token_green(SyntaxKind::COLON, ":").into(),
967 create_token_green(SyntaxKind::WHITESPACE, " ").into(),
968 create_scalar_green(value).into(),
969 create_token_green(SyntaxKind::NEWLINE, "\n").into(),
970 ]
971}
972
973fn create_mapping_entry(key: &str, value: &str) -> SyntaxNode {
975 let mut builder = GreenNodeBuilder::new();
976 builder.token(SyntaxKind::VALUE.into(), key); builder.token(SyntaxKind::COLON.into(), ":");
980 builder.token(SyntaxKind::WHITESPACE.into(), " ");
981 builder.start_node(SyntaxKind::SCALAR.into());
982 builder.token(SyntaxKind::VALUE.into(), value);
983 builder.finish_node();
984 builder.token(SyntaxKind::NEWLINE.into(), "\n");
985 SyntaxNode::new_root_mut(builder.finish())
986}
987
988impl Mapping {
990 pub fn set(&mut self, key: impl Into<ScalarValue>, value: impl Into<ScalarValue>) {
993 let key_scalar = key.into();
994 let value_scalar = value.into();
995 self.set_raw(&key_scalar.to_yaml_string(), &value_scalar.to_yaml_string());
996 }
997
998 pub fn set_value(&mut self, key: impl Into<ScalarValue>, value: YamlValue) {
1000 let key_scalar = key.into();
1001 self.set_raw(&key_scalar.to_yaml_string(), &value.to_yaml_string(0));
1004 }
1005
1006 pub fn set_raw(&mut self, key: &str, value: &str) {
1009 for (i, child) in self.0.children().enumerate() {
1011 if child.kind() == SyntaxKind::SCALAR {
1012 if let Some(scalar) = Scalar::cast(child.clone()) {
1013 if scalar.value().trim() == key {
1014 let mut new_entries = Vec::new();
1018 let mut found_key = false;
1019
1020 for pair_result in self.pairs() {
1022 if let (Some(k), Some(v_node)) = pair_result {
1023 if k.value().trim() == key && !found_key {
1024 let new_scalar = create_scalar_node(value);
1026 new_entries.push((k, new_scalar));
1027 found_key = true;
1028 } else {
1029 new_entries.push((k, v_node));
1030 }
1031 }
1032 }
1033
1034 if found_key {
1035 self.rebuild_from_pairs(new_entries);
1037 return;
1038 }
1039 }
1040 }
1041 }
1042 }
1043
1044 let mut pairs = Vec::new();
1047 for (k, v) in self.pairs() {
1048 if let (Some(key_scalar), Some(value_node)) = (k, v) {
1049 pairs.push((key_scalar, value_node));
1050 }
1051 }
1052
1053 let key_scalar = Scalar(create_scalar_node(key));
1055 let value_node = create_scalar_node(value);
1056 pairs.push((key_scalar, value_node));
1057
1058 self.rebuild_from_pairs(pairs);
1059 }
1060
1061 fn rebuild_from_pairs(&mut self, pairs: Vec<(Scalar, SyntaxNode)>) {
1063 let mut builder = GreenNodeBuilder::new();
1064 builder.start_node(SyntaxKind::MAPPING.into());
1065
1066 for (key_scalar, value_node) in pairs {
1067 builder.token(SyntaxKind::VALUE.into(), &key_scalar.value());
1069 builder.token(SyntaxKind::COLON.into(), ":");
1070 builder.token(SyntaxKind::WHITESPACE.into(), " ");
1071
1072 if value_node.kind() == SyntaxKind::SCALAR {
1074 builder.start_node(SyntaxKind::SCALAR.into());
1075 for token in value_node.children_with_tokens() {
1077 if let Some(token) = token.as_token() {
1078 if token.kind() == SyntaxKind::VALUE {
1079 builder.token(SyntaxKind::VALUE.into(), token.text());
1080 }
1081 }
1082 }
1083 builder.finish_node();
1084 }
1085
1086 builder.token(SyntaxKind::NEWLINE.into(), "\n");
1087 }
1088
1089 builder.finish_node();
1090 let new_mapping = SyntaxNode::new_root_mut(builder.finish());
1091
1092 let child_count = self.0.children_with_tokens().count();
1094 self.0
1095 .splice_children(0..child_count, vec![new_mapping.into()]);
1096 }
1097
1098 pub fn remove(&mut self, key: &str) -> bool {
1100 let children: Vec<_> = self.0.children_with_tokens().collect();
1101 let mut removal_range = None;
1102
1103 for (i, child) in children.iter().enumerate() {
1104 if let Some(node) = child.as_node() {
1105 if node.kind() == SyntaxKind::KEY {
1106 if node.text() == key {
1107 let start = i;
1109 let mut end = i + 1;
1110
1111 while end < children.len() {
1113 if let Some(token) = children[end].as_token() {
1114 end += 1;
1115 if token.kind() == SyntaxKind::NEWLINE {
1116 break;
1117 }
1118 } else {
1119 end += 1;
1120 }
1121 }
1122
1123 removal_range = Some(start..end);
1124 break;
1125 }
1126 }
1127 }
1128 }
1129
1130 if let Some(range) = removal_range {
1131 self.0.splice_children(range, vec![]);
1132 true
1133 } else {
1134 false
1135 }
1136 }
1137
1138 pub fn rename_key(&mut self, old_key: &str, new_key: impl Into<ScalarValue>) -> bool {
1140 let new_key_scalar = new_key.into();
1141 self.rename_key_raw(old_key, &new_key_scalar.to_yaml_string())
1142 }
1143
1144 pub fn rename_key_raw(&mut self, old_key: &str, new_key: &str) -> bool {
1147 let children: Vec<_> = self.0.children().collect();
1148
1149 for (i, child) in children.iter().enumerate() {
1150 if child.kind() == SyntaxKind::KEY {
1151 let key_text = child.text().to_string();
1153 if key_text == old_key {
1154 let mut builder = GreenNodeBuilder::new();
1156 builder.start_node(SyntaxKind::KEY.into());
1157 builder.token(SyntaxKind::STRING.into(), new_key);
1158 builder.finish_node();
1159 let new_key_node = SyntaxNode::new_root_mut(builder.finish());
1160
1161 self.0.splice_children(i..i + 1, vec![new_key_node.into()]);
1162 return true;
1163 }
1164 }
1165 }
1166 false
1167 }
1168
1169 pub fn set_path(&mut self, path: &str, value: impl Into<ScalarValue>) {
1171 let value_scalar = value.into();
1172 self.set_path_raw(path, &value_scalar.to_yaml_string());
1173 }
1174
1175 pub fn set_path_raw(&mut self, path: &str, value: &str) {
1178 let parts: Vec<&str> = path.split('.').collect();
1179 if parts.len() == 1 {
1180 self.set_raw(parts[0], value);
1181 return;
1182 }
1183
1184 let (_first, _rest) = parts.split_first().unwrap();
1186 }
1190}
1191
1192fn create_scalar_node(value: &str) -> SyntaxNode {
1193 SyntaxNode::new_root_mut(create_scalar_green(value))
1194}
1195
1196fn create_sequence_item_green(
1197 value: &str,
1198) -> Vec<rowan::NodeOrToken<rowan::GreenNode, rowan::GreenToken>> {
1199 vec![
1200 create_token_green(SyntaxKind::DASH, "-").into(),
1201 create_token_green(SyntaxKind::WHITESPACE, " ").into(),
1202 create_scalar_green(value).into(),
1203 create_token_green(SyntaxKind::NEWLINE, "\n").into(),
1204 ]
1205}
1206
1207impl Sequence {
1209 pub fn push(&mut self, value: &str) {
1211 let new_item_tokens = create_sequence_item_green(value);
1212 let child_count = self.0.children_with_tokens().count();
1213 let syntax_elements: Vec<_> = new_item_tokens
1215 .into_iter()
1216 .map(|element| {
1217 match element {
1218 rowan::NodeOrToken::Node(green_node) => {
1219 SyntaxNode::new_root_mut(green_node).into()
1220 }
1221 rowan::NodeOrToken::Token(green_token) => {
1222 let mut builder = GreenNodeBuilder::new();
1225 builder.start_node(SyntaxKind::ROOT.into());
1226 builder.token(green_token.kind(), green_token.text());
1227 builder.finish_node();
1228 let temp_node = SyntaxNode::new_root_mut(builder.finish());
1229 temp_node.first_token().unwrap().into()
1230 }
1231 }
1232 })
1233 .collect();
1234 self.0
1235 .splice_children(child_count..child_count, syntax_elements);
1236 }
1237
1238 pub fn insert(&mut self, index: usize, value: &str) {
1240 let children: Vec<_> = self.0.children_with_tokens().collect();
1241 let mut item_count = 0;
1242 let mut insert_pos = children.len();
1243
1244 for (i, child) in children.iter().enumerate() {
1246 if let Some(token) = child.as_token() {
1247 if token.kind() == SyntaxKind::DASH {
1248 if item_count == index {
1249 insert_pos = i;
1250 break;
1251 }
1252 item_count += 1;
1253 }
1254 }
1255 }
1256
1257 let new_item_tokens = create_sequence_item_green(value);
1258 let syntax_elements: Vec<_> = new_item_tokens
1260 .into_iter()
1261 .map(|element| match element {
1262 rowan::NodeOrToken::Node(green_node) => SyntaxNode::new_root_mut(green_node).into(),
1263 rowan::NodeOrToken::Token(green_token) => {
1264 let mut builder = GreenNodeBuilder::new();
1265 builder.start_node(SyntaxKind::ROOT.into());
1266 builder.token(green_token.kind(), green_token.text());
1267 builder.finish_node();
1268 let temp_node = SyntaxNode::new_root_mut(builder.finish());
1269 temp_node.first_token().unwrap().into()
1270 }
1271 })
1272 .collect();
1273 self.0
1274 .splice_children(insert_pos..insert_pos, syntax_elements);
1275 }
1276
1277 pub fn set(&mut self, index: usize, value: &str) -> bool {
1279 let children: Vec<_> = self.0.children_with_tokens().collect();
1280 let mut item_count = 0;
1281
1282 for (i, child) in children.iter().enumerate() {
1283 if let Some(token) = child.as_token() {
1284 if token.kind() == SyntaxKind::DASH {
1285 if item_count == index {
1286 let mut value_index = None;
1288 for (j, next_child) in children.iter().enumerate().skip(i + 1) {
1289 if let Some(node) = next_child.as_node() {
1290 if node.kind() == SyntaxKind::SCALAR {
1291 value_index = Some(j);
1292 break;
1293 }
1294 }
1295 }
1296
1297 if let Some(val_idx) = value_index {
1298 let new_value_node =
1300 SyntaxNode::new_root_mut(create_scalar_green(value));
1301 self.0
1302 .splice_children(val_idx..val_idx + 1, vec![new_value_node.into()]);
1303 return true;
1304 }
1305 }
1306 item_count += 1;
1307 }
1308 }
1309 }
1310 false
1311 }
1312
1313 pub fn remove(&mut self, index: usize) -> Option<String> {
1315 let children: Vec<_> = self.0.children().collect();
1316
1317 if self.is_flow_style() {
1319 let mut item_count = 0;
1320 for (i, child) in children.iter().enumerate() {
1321 if child.kind() == SyntaxKind::SCALAR {
1322 if item_count == index {
1323 if let Some(scalar) = Scalar::cast(child.clone()) {
1324 let value = scalar.value();
1325
1326 let mut start = i;
1328 let mut end = i + 1;
1329
1330 if start > 0 && children[start - 1].kind() == SyntaxKind::COMMA {
1332 start -= 1;
1333 }
1334 else if end < children.len()
1336 && children[end].kind() == SyntaxKind::COMMA
1337 {
1338 end += 1;
1339 }
1340
1341 self.0.splice_children(start..end, vec![]);
1342 return Some(value);
1343 }
1344 }
1345 item_count += 1;
1346 }
1347 }
1348 } else {
1349 let mut item_count = 0;
1351 for (i, child) in children.iter().enumerate() {
1352 if child.kind() == SyntaxKind::DASH {
1353 if item_count == index {
1354 let start = i;
1356 let mut end = i + 1;
1357 let mut removed_value = None;
1358
1359 while end < children.len() {
1360 let child_kind = children[end].kind();
1361 if child_kind == SyntaxKind::SCALAR {
1362 if let Some(scalar) = Scalar::cast(children[end].clone()) {
1363 removed_value = Some(scalar.value());
1364 }
1365 }
1366 end += 1;
1367 if child_kind == SyntaxKind::NEWLINE {
1368 break;
1369 }
1370 }
1371
1372 self.0.splice_children(start..end, vec![]);
1373 return removed_value;
1374 }
1375 item_count += 1;
1376 }
1377 }
1378 }
1379 None
1380 }
1381
1382 fn is_flow_style(&self) -> bool {
1384 self.0
1385 .children()
1386 .any(|child| child.kind() == SyntaxKind::LEFT_BRACKET)
1387 }
1388}
1389
1390impl Scalar {
1392 pub fn set_value(&mut self, value: &str) {
1394 let children_count = self.0.children_with_tokens().count();
1395 let mut builder = GreenNodeBuilder::new();
1397 builder.start_node(SyntaxKind::ROOT.into());
1398 builder.token(SyntaxKind::VALUE.into(), value);
1399 builder.finish_node();
1400 let temp_node = SyntaxNode::new_root_mut(builder.finish());
1401 let new_token = temp_node.first_token().unwrap();
1402 self.0
1403 .splice_children(0..children_count, vec![new_token.into()]);
1404 }
1405}