yaml_edit/nodes/
tagged_node.rs1use super::{Lang, SyntaxNode};
2use crate::as_yaml::{AsYaml, YamlKind};
3use crate::lex::SyntaxKind;
4use crate::yaml::{Mapping, MappingEntry, Scalar, Sequence, Set};
5use rowan::ast::AstNode;
6
7ast_node!(
8 TaggedNode,
9 TAGGED_NODE,
10 "A YAML tagged scalar (tag + value)"
11);
12impl TaggedNode {
13 pub fn tag(&self) -> Option<String> {
15 for child in self.0.children_with_tokens() {
17 if let rowan::NodeOrToken::Token(token) = child {
18 if token.kind() == SyntaxKind::TAG {
19 return Some(token.text().to_string());
20 }
21 }
22 }
23 None
24 }
25
26 pub fn value(&self) -> Option<Scalar> {
28 for child in self.0.children() {
30 if child.kind() == SyntaxKind::SCALAR {
31 return Scalar::cast(child);
32 }
33 }
34 None
35 }
36
37 pub fn as_string(&self) -> Option<String> {
40 if let Some(scalar) = self.value() {
41 Some(scalar.as_string())
42 } else {
43 self.extract_deepest_string_value()
45 }
46 }
47
48 fn extract_deepest_string_value(&self) -> Option<String> {
50 Self::find_string_token_recursive(&self.0)
51 }
52
53 fn find_string_token_recursive(node: &rowan::SyntaxNode<crate::yaml::Lang>) -> Option<String> {
55 for child in node.children_with_tokens() {
57 if let rowan::NodeOrToken::Token(token) = child {
58 if token.kind() == SyntaxKind::STRING {
59 return Some(token.text().to_string());
60 }
61 }
62 }
63
64 for child in node.children() {
66 if let Some(result) = Self::find_string_token_recursive(&child) {
67 return Some(result);
68 }
69 }
70
71 None
72 }
73
74 pub fn as_set(&self) -> Option<Set> {
76 Set::cast(self.0.clone())
77 }
78
79 pub fn as_ordered_mapping(&self) -> Option<Vec<MappingEntry>> {
84 if self.tag().as_deref() != Some("!!omap") {
85 return None;
86 }
87 Some(self.extract_mapping_entries())
88 }
89
90 pub fn as_pairs(&self) -> Option<Vec<MappingEntry>> {
95 if self.tag().as_deref() != Some("!!pairs") {
96 return None;
97 }
98 Some(self.extract_mapping_entries())
99 }
100
101 fn extract_mapping_entries(&self) -> Vec<MappingEntry> {
104 let mut entries = Vec::new();
105 for child in self.0.children() {
106 if let Some(sequence) = Sequence::cast(child) {
107 for item in sequence.items() {
108 if let Some(mapping) = Mapping::cast(item) {
109 if let Some(entry) = mapping.entries().next() {
110 entries.push(entry);
111 }
112 }
113 }
114 break;
115 }
116 }
117 entries
118 }
119}
120
121impl AsYaml for TaggedNode {
122 fn as_node(&self) -> Option<&SyntaxNode> {
123 Some(&self.0)
124 }
125
126 fn kind(&self) -> YamlKind {
127 self.tag()
128 .map(|t| YamlKind::Tagged(std::borrow::Cow::Owned(t)))
129 .unwrap_or(YamlKind::Scalar)
130 }
131
132 fn build_content(
133 &self,
134 builder: &mut rowan::GreenNodeBuilder,
135 _indent: usize,
136 _flow_context: bool,
137 ) -> bool {
138 crate::as_yaml::copy_node_content(builder, &self.0);
139 false
140 }
141
142 fn is_inline(&self) -> bool {
143 true
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use std::str::FromStr;
151
152 use rowan::ast::AstNode;
153
154 use crate::YamlFile;
155
156 #[test]
157 fn test_tagged_node_as_string_plain() {
158 let yaml = YamlFile::from_str("key: !custom hello").unwrap();
159 let doc = yaml.documents().next().unwrap();
160 let mapping = doc.as_mapping().unwrap();
161 let val = mapping.get_node("key").unwrap();
162 let tagged = crate::yaml::TaggedNode::cast(val).unwrap();
163 assert_eq!(tagged.as_string(), Some("hello".to_string()));
164 }
165
166 #[test]
167 fn test_tagged_node_as_string_double_quoted() {
168 let yaml = YamlFile::from_str(r#"key: !custom "hello world""#).unwrap();
170 let doc = yaml.documents().next().unwrap();
171 let mapping = doc.as_mapping().unwrap();
172 let val = mapping.get_node("key").unwrap();
173 let tagged = crate::yaml::TaggedNode::cast(val).unwrap();
174 assert_eq!(tagged.as_string(), Some("hello world".to_string()));
175 }
176}