use super::{Lang, SyntaxNode};
use crate::as_yaml::{AsYaml, YamlKind};
use crate::lex::SyntaxKind;
use crate::yaml::{Mapping, MappingEntry, Scalar, Sequence, Set};
use rowan::ast::AstNode;
ast_node!(
TaggedNode,
TAGGED_NODE,
"A YAML tagged scalar (tag + value)"
);
impl TaggedNode {
pub fn tag(&self) -> Option<String> {
for child in self.0.children_with_tokens() {
if let rowan::NodeOrToken::Token(token) = child {
if token.kind() == SyntaxKind::TAG {
return Some(token.text().to_string());
}
}
}
None
}
pub fn value(&self) -> Option<Scalar> {
for child in self.0.children() {
if child.kind() == SyntaxKind::SCALAR {
return Scalar::cast(child);
}
}
None
}
pub fn as_string(&self) -> Option<String> {
if let Some(scalar) = self.value() {
Some(scalar.as_string())
} else {
self.extract_deepest_string_value()
}
}
fn extract_deepest_string_value(&self) -> Option<String> {
Self::find_string_token_recursive(&self.0)
}
fn find_string_token_recursive(node: &rowan::SyntaxNode<crate::yaml::Lang>) -> Option<String> {
for child in node.children_with_tokens() {
if let rowan::NodeOrToken::Token(token) = child {
if token.kind() == SyntaxKind::STRING {
return Some(token.text().to_string());
}
}
}
for child in node.children() {
if let Some(result) = Self::find_string_token_recursive(&child) {
return Some(result);
}
}
None
}
pub fn as_set(&self) -> Option<Set> {
Set::cast(self.0.clone())
}
pub fn as_ordered_mapping(&self) -> Option<Vec<MappingEntry>> {
if self.tag().as_deref() != Some("!!omap") {
return None;
}
Some(self.extract_mapping_entries())
}
pub fn as_pairs(&self) -> Option<Vec<MappingEntry>> {
if self.tag().as_deref() != Some("!!pairs") {
return None;
}
Some(self.extract_mapping_entries())
}
fn extract_mapping_entries(&self) -> Vec<MappingEntry> {
let mut entries = Vec::new();
for child in self.0.children() {
if let Some(sequence) = Sequence::cast(child) {
for item in sequence.items() {
if let Some(mapping) = Mapping::cast(item) {
if let Some(entry) = mapping.entries().next() {
entries.push(entry);
}
}
}
break;
}
}
entries
}
}
impl AsYaml for TaggedNode {
fn as_node(&self) -> Option<&SyntaxNode> {
Some(&self.0)
}
fn kind(&self) -> YamlKind {
self.tag()
.map(|t| YamlKind::Tagged(std::borrow::Cow::Owned(t)))
.unwrap_or(YamlKind::Scalar)
}
fn build_content(
&self,
builder: &mut rowan::GreenNodeBuilder,
_indent: usize,
_flow_context: bool,
) -> bool {
crate::as_yaml::copy_node_content(builder, &self.0);
false
}
fn is_inline(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use rowan::ast::AstNode;
use crate::YamlFile;
#[test]
fn test_tagged_node_as_string_plain() {
let yaml = YamlFile::from_str("key: !custom hello").unwrap();
let doc = yaml.documents().next().unwrap();
let mapping = doc.as_mapping().unwrap();
let val = mapping.get_node("key").unwrap();
let tagged = crate::yaml::TaggedNode::cast(val).unwrap();
assert_eq!(tagged.as_string(), Some("hello".to_string()));
}
#[test]
fn test_tagged_node_as_string_double_quoted() {
let yaml = YamlFile::from_str(r#"key: !custom "hello world""#).unwrap();
let doc = yaml.documents().next().unwrap();
let mapping = doc.as_mapping().unwrap();
let val = mapping.get_node("key").unwrap();
let tagged = crate::yaml::TaggedNode::cast(val).unwrap();
assert_eq!(tagged.as_string(), Some("hello world".to_string()));
}
}