yaml_edit/nodes/
directive.rs1use super::{Lang, SyntaxNode};
2use crate::lex::SyntaxKind;
3use rowan::ast::AstNode;
4use rowan::GreenNodeBuilder;
5
6ast_node!(Directive, DIRECTIVE, "A YAML directive like %YAML 1.2");
7
8impl Directive {
9 pub fn text(&self) -> String {
11 self.0.text().to_string()
12 }
13
14 pub fn name(&self) -> Option<String> {
16 let text = self.text();
17 if let Some(directive_content) = text.strip_prefix('%') {
18 directive_content
19 .split_whitespace()
20 .next()
21 .map(|s| s.to_string())
22 } else {
23 None
24 }
25 }
26
27 pub fn value(&self) -> Option<String> {
29 let text = self.text();
30 if let Some(directive_content) = text.strip_prefix('%') {
31 let parts: Vec<&str> = directive_content.split_whitespace().collect();
32 if parts.len() > 1 {
33 Some(parts[1..].join(" "))
34 } else {
35 None
36 }
37 } else {
38 None
39 }
40 }
41
42 pub fn is_yaml_version(&self) -> bool {
44 self.name().as_deref() == Some("YAML")
45 }
46
47 pub fn is_tag(&self) -> bool {
49 self.name().as_deref() == Some("TAG")
50 }
51
52 pub fn new_yaml_version(version: &str) -> Self {
54 let directive_text = format!("%YAML {}", version);
55 let mut builder = GreenNodeBuilder::new();
56 builder.start_node(SyntaxKind::DIRECTIVE.into());
57 builder.token(SyntaxKind::DIRECTIVE.into(), &directive_text);
58 builder.finish_node();
59 Directive(SyntaxNode::new_root_mut(builder.finish()))
60 }
61
62 pub fn new_tag(handle: &str, prefix: &str) -> Self {
64 let directive_text = format!("%TAG {} {}", handle, prefix);
65 let mut builder = GreenNodeBuilder::new();
66 builder.start_node(SyntaxKind::DIRECTIVE.into());
67 builder.token(SyntaxKind::DIRECTIVE.into(), &directive_text);
68 builder.finish_node();
69 Directive(SyntaxNode::new_root_mut(builder.finish()))
70 }
71}
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use crate::nodes::Document;
76 use crate::yaml::YamlFile;
77 use std::str::FromStr;
78
79 #[test]
80 fn test_parse_yaml_version_directive() {
81 let yaml = "%YAML 1.2\n---\nkey: value";
82 let parsed = YamlFile::from_str(yaml).unwrap();
83
84 let directives: Vec<_> = parsed.directives().collect();
85 assert_eq!(directives.len(), 1);
86
87 let directive = &directives[0];
88 assert!(directive.is_yaml_version());
89 assert_eq!(directive.name(), Some("YAML".to_string()));
90 assert_eq!(directive.value(), Some("1.2".to_string()));
91 assert_eq!(directive.text(), "%YAML 1.2");
92 }
93 #[test]
94 fn test_parse_tag_directive() {
95 let yaml = "%TAG ! tag:example.com,2000:app/\n---\nkey: value";
96 let parsed = YamlFile::from_str(yaml).unwrap();
97
98 let directives: Vec<_> = parsed.directives().collect();
99 assert_eq!(directives.len(), 1);
100
101 let directive = &directives[0];
102 assert!(directive.is_tag());
103 assert_eq!(directive.name(), Some("TAG".to_string()));
104 assert_eq!(
105 directive.value(),
106 Some("! tag:example.com,2000:app/".to_string())
107 );
108 }
109 #[test]
110 fn test_multiple_directives() {
111 let yaml = "%YAML 1.2\n%TAG ! tag:example.com,2000:app/\n---\nkey: value";
112 let parsed = YamlFile::from_str(yaml).unwrap();
113
114 let directives: Vec<_> = parsed.directives().collect();
115 assert_eq!(directives.len(), 2);
116
117 assert!(directives[0].is_yaml_version());
118 assert!(directives[1].is_tag());
119 }
120 #[test]
121 fn test_create_yaml_version_directive() {
122 let directive = Directive::new_yaml_version("1.2");
123 assert!(directive.is_yaml_version());
124 assert_eq!(directive.value(), Some("1.2".to_string()));
125 assert_eq!(directive.text(), "%YAML 1.2");
126 }
127 #[test]
128 fn test_create_tag_directive() {
129 let directive = Directive::new_tag("!", "tag:example.com,2000:app/");
130 assert!(directive.is_tag());
131 assert_eq!(directive.text(), "%TAG ! tag:example.com,2000:app/");
132 }
133 #[test]
134 fn test_add_directive_to_yaml() {
135 let yaml = YamlFile::new();
136 yaml.add_directive("%YAML 1.2");
137
138 let directives: Vec<_> = yaml.directives().collect();
139 assert_eq!(directives.len(), 1);
140 assert_eq!(yaml.to_string(), "%YAML 1.2");
141 }
142 #[test]
143 fn test_yaml_with_directive_and_content() {
144 let yaml = YamlFile::new();
145 yaml.add_directive("%YAML 1.2");
146
147 let doc = Document::new_mapping();
148 doc.set("name", "test");
149 yaml.push_document(doc);
150
151 let output = yaml.to_string();
152 assert_eq!(output, "%YAML 1.2name: test\n");
153
154 let directives: Vec<_> = yaml.directives().collect();
156 let documents: Vec<_> = yaml.documents().collect();
157 assert_eq!(directives.len(), 1);
158 assert_eq!(documents.len(), 1);
159 }
160 #[test]
161 fn test_directive_preservation_in_parsing() {
162 let input = "%YAML 1.2\n%TAG ! tag:example.com,2000:app/\n---\nkey: value\n";
163 let parsed = YamlFile::from_str(input).unwrap();
164 let output = parsed.to_string();
165
166 assert_eq!(output, input);
168 }
169}