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_mapping(&self) -> Option<Mapping> {
40 self.0.children().find_map(Mapping::cast)
41 }
42
43 pub fn as_sequence(&self) -> Option<Sequence> {
46 self.0.children().find_map(Sequence::cast)
47 }
48
49 pub fn as_string(&self) -> Option<String> {
52 if let Some(scalar) = self.value() {
53 Some(scalar.as_string())
54 } else {
55 self.extract_deepest_string_value()
57 }
58 }
59
60 fn extract_deepest_string_value(&self) -> Option<String> {
62 Self::find_string_token_recursive(&self.0)
63 }
64
65 fn find_string_token_recursive(node: &rowan::SyntaxNode<crate::yaml::Lang>) -> Option<String> {
67 for child in node.children_with_tokens() {
69 if let rowan::NodeOrToken::Token(token) = child {
70 if token.kind() == SyntaxKind::STRING {
71 return Some(token.text().to_string());
72 }
73 }
74 }
75
76 for child in node.children() {
78 if let Some(result) = Self::find_string_token_recursive(&child) {
79 return Some(result);
80 }
81 }
82
83 None
84 }
85
86 pub fn as_set(&self) -> Option<Set> {
88 Set::cast(self.0.clone())
89 }
90
91 pub fn as_ordered_mapping(&self) -> Option<Vec<MappingEntry>> {
96 if self.tag().as_deref() != Some("!!omap") {
97 return None;
98 }
99 Some(self.extract_mapping_entries())
100 }
101
102 pub fn as_pairs(&self) -> Option<Vec<MappingEntry>> {
107 if self.tag().as_deref() != Some("!!pairs") {
108 return None;
109 }
110 Some(self.extract_mapping_entries())
111 }
112
113 fn extract_mapping_entries(&self) -> Vec<MappingEntry> {
116 let mut entries = Vec::new();
117 for child in self.0.children() {
118 if let Some(sequence) = Sequence::cast(child) {
119 for item in sequence.items() {
120 if let Some(mapping) = Mapping::cast(item) {
121 if let Some(entry) = mapping.entries().next() {
122 entries.push(entry);
123 }
124 }
125 }
126 break;
127 }
128 }
129 entries
130 }
131}
132
133impl AsYaml for TaggedNode {
134 fn as_node(&self) -> Option<&SyntaxNode> {
135 Some(&self.0)
136 }
137
138 fn kind(&self) -> YamlKind {
139 self.tag().map_or(YamlKind::Scalar, |t| {
140 YamlKind::Tagged(std::borrow::Cow::Owned(t))
141 })
142 }
143
144 fn build_content(
145 &self,
146 builder: &mut rowan::GreenNodeBuilder,
147 _indent: usize,
148 _flow_context: bool,
149 ) -> bool {
150 crate::as_yaml::copy_node_content(builder, &self.0);
151 false
152 }
153
154 fn is_inline(&self) -> bool {
155 true
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use std::str::FromStr;
163
164 use rowan::ast::AstNode;
165
166 use crate::YamlFile;
167
168 #[test]
169 fn test_tagged_node_as_string_plain() {
170 let yaml = YamlFile::from_str("key: !custom hello").unwrap();
171 let doc = yaml.documents().next().unwrap();
172 let mapping = doc.as_mapping().unwrap();
173 let val = mapping.get_node("key").unwrap();
174 let tagged = crate::yaml::TaggedNode::cast(val).unwrap();
175 assert_eq!(tagged.as_string(), Some("hello".to_string()));
176 }
177
178 #[test]
179 fn test_tagged_node_as_string_double_quoted() {
180 let yaml = YamlFile::from_str(r#"key: !custom "hello world""#).unwrap();
182 let doc = yaml.documents().next().unwrap();
183 let mapping = doc.as_mapping().unwrap();
184 let val = mapping.get_node("key").unwrap();
185 let tagged = crate::yaml::TaggedNode::cast(val).unwrap();
186 assert_eq!(tagged.as_string(), Some("hello world".to_string()));
187 }
188}