Skip to main content

yaml_edit/nodes/
directive.rs

1use 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    /// Get the full directive text (e.g., "%YAML 1.2")
10    pub fn text(&self) -> String {
11        self.0.text().to_string()
12    }
13
14    /// Get the directive name (e.g., "YAML" from "%YAML 1.2")
15    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    /// Get the directive value (e.g., "1.2" from "%YAML 1.2")
28    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    /// Check if this is a YAML version directive
43    pub fn is_yaml_version(&self) -> bool {
44        self.name().as_deref() == Some("YAML")
45    }
46
47    /// Check if this is a TAG directive
48    pub fn is_tag(&self) -> bool {
49        self.name().as_deref() == Some("TAG")
50    }
51
52    /// Create a new YAML version directive
53    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    /// Create a new TAG directive
63    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        // Should have both directive and document
155        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        // Check that directives are preserved
167        assert_eq!(output, input);
168    }
169}