use crate::scalar::ScalarValue;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum YamlValue {
Scalar(ScalarValue),
Sequence(Vec<YamlValue>),
Mapping(BTreeMap<String, YamlValue>),
Set(BTreeSet<String>),
OrderedMapping(Vec<(String, YamlValue)>),
Pairs(Vec<(String, YamlValue)>),
}
impl YamlValue {
pub fn cast(node: rowan::SyntaxNode<crate::yaml::Lang>) -> Option<Self> {
use crate::lex::SyntaxKind;
use crate::yaml::{Mapping, Scalar, Sequence, TaggedNode};
use rowan::ast::AstNode;
match node.kind() {
SyntaxKind::SCALAR => {
Scalar::cast(node)
.map(|scalar| YamlValue::Scalar(ScalarValue::from_scalar(&scalar)))
}
SyntaxKind::SEQUENCE => {
if let Some(seq) = Sequence::cast(node) {
let items: Vec<YamlValue> = seq.items().filter_map(YamlValue::cast).collect();
Some(YamlValue::Sequence(items))
} else {
None
}
}
SyntaxKind::MAPPING => {
if let Some(mapping) = Mapping::cast(node) {
let mut map = std::collections::BTreeMap::new();
for (key_node, value_node) in mapping.pairs() {
let key_str = if let Some(key_scalar) = Scalar::cast(key_node.clone()) {
key_scalar.as_string()
} else {
key_node.text().to_string()
};
if let Some(value_yaml) = YamlValue::cast(value_node) {
map.insert(key_str, value_yaml);
}
}
Some(YamlValue::Mapping(map))
} else {
None
}
}
SyntaxKind::TAGGED_NODE => {
TaggedNode::cast(node).and_then(|tagged| {
if let Some(set) = tagged.as_set() {
let members: BTreeSet<String> = set
.members()
.filter_map(|v| v.as_scalar().map(|s| s.as_string()))
.collect();
return Some(YamlValue::Set(members));
}
if let Some(entries) = tagged.as_ordered_mapping() {
let pairs: Vec<(String, YamlValue)> = entries
.into_iter()
.filter_map(|e| {
let key_node = e.key()?;
let key_scalar = Scalar::cast(key_node.children().next()?)?;
let key = key_scalar.as_string();
let val = e
.value()
.and_then(|v| v.children().next().and_then(YamlValue::cast))?;
Some((key, val))
})
.collect();
return Some(YamlValue::OrderedMapping(pairs));
}
if let Some(entries) = tagged.as_pairs() {
let pairs: Vec<(String, YamlValue)> = entries
.into_iter()
.filter_map(|e| {
let key_node = e.key()?;
let key_scalar = Scalar::cast(key_node.children().next()?)?;
let key = key_scalar.as_string();
let val = e
.value()
.and_then(|v| v.children().next().and_then(YamlValue::cast))?;
Some((key, val))
})
.collect();
return Some(YamlValue::Pairs(pairs));
}
tagged
.as_string()
.map(|s| YamlValue::Scalar(ScalarValue::string(s)))
})
}
SyntaxKind::VALUE => {
node.children().next().and_then(YamlValue::cast)
}
_ => None,
}
}
pub fn scalar(value: impl Into<ScalarValue>) -> Self {
YamlValue::Scalar(value.into())
}
pub const fn sequence() -> Self {
YamlValue::Sequence(Vec::new())
}
pub const fn from_sequence(items: Vec<YamlValue>) -> Self {
YamlValue::Sequence(items)
}
pub const fn mapping() -> Self {
YamlValue::Mapping(BTreeMap::new())
}
pub fn parse_raw(yaml_str: &str) -> Self {
use std::str::FromStr;
if let Ok(parsed) = crate::YamlFile::from_str(yaml_str) {
if let Some(doc) = parsed.document() {
return Self::from_document(&doc);
}
}
YamlValue::Scalar(ScalarValue::parse(yaml_str))
}
pub fn from_document(doc: &crate::yaml::Document) -> Self {
use rowan::ast::AstNode;
doc.syntax()
.children()
.find_map(Self::cast)
.unwrap_or_else(|| YamlValue::Scalar(ScalarValue::string("")))
}
pub const fn from_mapping(map: BTreeMap<String, YamlValue>) -> Self {
YamlValue::Mapping(map)
}
pub const fn set() -> Self {
YamlValue::Set(BTreeSet::new())
}
pub const fn from_set(set: BTreeSet<String>) -> Self {
YamlValue::Set(set)
}
pub const fn ordered_mapping() -> Self {
YamlValue::OrderedMapping(Vec::new())
}
pub const fn from_ordered_mapping(pairs: Vec<(String, YamlValue)>) -> Self {
YamlValue::OrderedMapping(pairs)
}
pub const fn pairs() -> Self {
YamlValue::Pairs(Vec::new())
}
pub const fn from_pairs(pairs: Vec<(String, YamlValue)>) -> Self {
YamlValue::Pairs(pairs)
}
#[inline]
pub fn is_scalar(&self) -> bool {
matches!(self, YamlValue::Scalar(_))
}
#[inline]
pub fn is_sequence(&self) -> bool {
matches!(self, YamlValue::Sequence(_))
}
#[inline]
pub fn is_mapping(&self) -> bool {
matches!(self, YamlValue::Mapping(_))
}
#[inline]
pub fn is_set(&self) -> bool {
matches!(self, YamlValue::Set(_))
}
#[inline]
pub fn is_ordered_mapping(&self) -> bool {
matches!(self, YamlValue::OrderedMapping(_))
}
#[inline]
pub fn is_pairs(&self) -> bool {
matches!(self, YamlValue::Pairs(_))
}
pub fn as_scalar(&self) -> Option<&ScalarValue> {
match self {
YamlValue::Scalar(s) => Some(s),
_ => None,
}
}
pub fn as_sequence(&self) -> Option<&[YamlValue]> {
match self {
YamlValue::Sequence(seq) => Some(seq),
_ => None,
}
}
pub fn as_sequence_mut(&mut self) -> Option<&mut Vec<YamlValue>> {
match self {
YamlValue::Sequence(seq) => Some(seq),
_ => None,
}
}
pub fn as_mapping(&self) -> Option<&BTreeMap<String, YamlValue>> {
match self {
YamlValue::Mapping(map) => Some(map),
_ => None,
}
}
pub fn as_mapping_mut(&mut self) -> Option<&mut BTreeMap<String, YamlValue>> {
match self {
YamlValue::Mapping(map) => Some(map),
_ => None,
}
}
pub fn as_set(&self) -> Option<&BTreeSet<String>> {
match self {
YamlValue::Set(set) => Some(set),
_ => None,
}
}
pub fn as_set_mut(&mut self) -> Option<&mut BTreeSet<String>> {
match self {
YamlValue::Set(set) => Some(set),
_ => None,
}
}
pub fn as_ordered_mapping(&self) -> Option<&[(String, YamlValue)]> {
match self {
YamlValue::OrderedMapping(pairs) => Some(pairs),
_ => None,
}
}
pub fn as_ordered_mapping_mut(&mut self) -> Option<&mut Vec<(String, YamlValue)>> {
match self {
YamlValue::OrderedMapping(pairs) => Some(pairs),
_ => None,
}
}
pub fn as_pairs(&self) -> Option<&[(String, YamlValue)]> {
match self {
YamlValue::Pairs(pairs) => Some(pairs),
_ => None,
}
}
pub fn as_pairs_mut(&mut self) -> Option<&mut Vec<(String, YamlValue)>> {
match self {
YamlValue::Pairs(pairs) => Some(pairs),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
YamlValue::Scalar(s) => Some(s.value()),
_ => None,
}
}
pub fn to_i64(&self) -> Option<i64> {
match self {
YamlValue::Scalar(s) => {
use crate::scalar::ScalarType;
if s.scalar_type() == ScalarType::Integer {
ScalarValue::parse_integer(s.value())
} else {
None
}
}
_ => None,
}
}
pub fn to_f64(&self) -> Option<f64> {
match self {
YamlValue::Scalar(s) => {
use crate::scalar::ScalarType;
if s.scalar_type() == ScalarType::Float {
s.value().trim().parse::<f64>().ok()
} else {
None
}
}
_ => None,
}
}
pub fn to_bool(&self) -> Option<bool> {
match self {
YamlValue::Scalar(s) => {
use crate::scalar::ScalarType;
if s.scalar_type() == ScalarType::Boolean {
match s.value().to_lowercase().as_str() {
"true" | "yes" | "on" => Some(true),
"false" | "no" | "off" => Some(false),
_ => None,
}
} else {
None
}
}
_ => None,
}
}
pub fn to_yaml_string(&self, indent: usize) -> String {
match self {
YamlValue::Scalar(s) => s.to_yaml_string(),
YamlValue::Sequence(seq) => {
if seq.is_empty() {
"[]".to_string()
} else {
let mut result = String::with_capacity(seq.len() * 20); let indent_str = " ".repeat(indent);
for item in seq {
result.push_str(&indent_str);
result.push_str(" - ");
match item {
YamlValue::Scalar(s) => result.push_str(&s.to_yaml_string()),
_ => {
result.push('\n');
result.push_str(&item.to_yaml_string(indent + 4));
}
}
result.push('\n');
}
result.truncate(result.trim_end().len());
result
}
}
YamlValue::Mapping(map) => {
if map.is_empty() {
"{}".to_string()
} else {
let mut result = String::with_capacity(map.len() * 30); let indent_str = " ".repeat(indent);
for (key, value) in map {
result.push_str(&indent_str);
result.push_str(key);
result.push_str(": ");
match value {
YamlValue::Scalar(s) => result.push_str(&s.to_yaml_string()),
_ => {
result.push('\n');
result.push_str(&value.to_yaml_string(indent + 2));
}
}
result.push('\n');
}
result.truncate(result.trim_end().len());
result
}
}
YamlValue::Set(set) => {
if set.is_empty() {
"!!set {}".to_string()
} else {
let mut result = String::from("!!set");
result.push('\n');
let indent_str = " ".repeat(indent);
for item in set {
result.push_str(&indent_str);
result.push_str(item);
result.push_str(": null");
result.push('\n');
}
result.truncate(result.trim_end().len());
result
}
}
YamlValue::OrderedMapping(pairs) => {
if pairs.is_empty() {
"!!omap []".to_string()
} else {
let mut result = String::from("!!omap");
result.push('\n');
let indent_str = " ".repeat(indent);
for (key, value) in pairs {
result.push_str(&indent_str);
result.push_str(" - ");
result.push_str(key);
result.push_str(": ");
match value {
YamlValue::Scalar(s) => result.push_str(&s.to_yaml_string()),
_ => {
result.push('\n');
result.push_str(&value.to_yaml_string(indent + 4));
}
}
result.push('\n');
}
result.truncate(result.trim_end().len());
result
}
}
YamlValue::Pairs(pairs) => {
if pairs.is_empty() {
"!!pairs []".to_string()
} else {
let mut result = String::from("!!pairs");
result.push('\n');
let indent_str = " ".repeat(indent);
for (key, value) in pairs {
result.push_str(&indent_str);
result.push_str(" - ");
result.push_str(key);
result.push_str(": ");
match value {
YamlValue::Scalar(s) => result.push_str(&s.to_yaml_string()),
_ => {
result.push('\n');
result.push_str(&value.to_yaml_string(indent + 4));
}
}
result.push('\n');
}
result.truncate(result.trim_end().len());
result
}
}
}
}
}
impl fmt::Display for YamlValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_yaml_string(0))
}
}
impl From<String> for YamlValue {
fn from(value: String) -> Self {
YamlValue::Scalar(ScalarValue::from(value))
}
}
impl From<&str> for YamlValue {
fn from(value: &str) -> Self {
YamlValue::Scalar(ScalarValue::from(value))
}
}
impl From<i32> for YamlValue {
fn from(value: i32) -> Self {
YamlValue::Scalar(ScalarValue::from(value))
}
}
impl From<i64> for YamlValue {
fn from(value: i64) -> Self {
YamlValue::Scalar(ScalarValue::from(value))
}
}
impl From<f32> for YamlValue {
fn from(value: f32) -> Self {
YamlValue::Scalar(ScalarValue::from(value))
}
}
impl From<f64> for YamlValue {
fn from(value: f64) -> Self {
YamlValue::Scalar(ScalarValue::from(value))
}
}
impl From<bool> for YamlValue {
fn from(value: bool) -> Self {
YamlValue::Scalar(ScalarValue::from(value))
}
}
impl From<ScalarValue> for YamlValue {
fn from(value: ScalarValue) -> Self {
YamlValue::Scalar(value)
}
}
impl From<&YamlValue> for YamlValue {
fn from(value: &YamlValue) -> Self {
value.clone()
}
}
impl<T> From<Vec<T>> for YamlValue
where
T: Into<YamlValue>,
{
fn from(vec: Vec<T>) -> Self {
let values: Vec<_> = vec.into_iter().map(Into::into).collect();
YamlValue::Sequence(values)
}
}
impl<K, V> From<BTreeMap<K, V>> for YamlValue
where
K: Into<String>,
V: Into<YamlValue>,
{
fn from(map: BTreeMap<K, V>) -> Self {
YamlValue::Mapping(map.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
}
}
impl<T> From<BTreeSet<T>> for YamlValue
where
T: Into<String>,
{
fn from(set: BTreeSet<T>) -> Self {
YamlValue::Set(set.into_iter().map(Into::into).collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scalar_value() {
let val = YamlValue::from("hello");
assert!(val.is_scalar());
assert_eq!(val.to_yaml_string(0), "hello");
let val = YamlValue::from(42);
assert!(val.is_scalar());
assert_eq!(val.to_yaml_string(0), "42");
let val = YamlValue::from(true);
assert!(val.is_scalar());
assert_eq!(val.to_yaml_string(0), "true");
}
#[test]
fn test_sequence_value() {
let val = YamlValue::from(vec!["item1", "item2", "item3"]);
assert!(val.is_sequence());
let yaml = val.to_yaml_string(0);
assert_eq!(yaml, " - item1\n - item2\n - item3");
let val = YamlValue::sequence();
assert_eq!(val.to_yaml_string(0), "[]");
}
#[test]
fn test_mapping_value() {
let mut map = BTreeMap::new();
map.insert("name", YamlValue::from("project"));
map.insert("version", YamlValue::from("1.0.0"));
let val = YamlValue::from(map);
assert!(val.is_mapping());
let yaml = val.to_yaml_string(0);
assert_eq!(yaml, "name: project\nversion: 1.0.0");
let val = YamlValue::mapping();
assert_eq!(val.to_yaml_string(0), "{}");
}
#[test]
fn test_nested_structure() {
let mut db_map = BTreeMap::new();
db_map.insert("host", YamlValue::from("localhost"));
db_map.insert("port", YamlValue::from(5432));
let mut root_map = BTreeMap::new();
root_map.insert("name", YamlValue::from("app"));
root_map.insert("database", YamlValue::from(db_map));
root_map.insert("features", YamlValue::from(vec!["auth", "logging"]));
let val = YamlValue::from(root_map);
let yaml = val.to_yaml_string(0);
assert_eq!(
yaml,
"database: \n host: localhost\n port: 5432\nfeatures: \n - auth\n - logging\nname: app"
);
}
#[test]
fn test_set_value() {
let mut set = BTreeSet::new();
set.insert("item1".to_string());
set.insert("item2".to_string());
set.insert("item3".to_string());
let val = YamlValue::from_set(set);
assert!(val.is_set());
let yaml = val.to_yaml_string(0);
assert_eq!(yaml, "!!set\nitem1: null\nitem2: null\nitem3: null");
let val = YamlValue::set();
assert_eq!(val.to_yaml_string(0), "!!set {}");
}
#[test]
fn test_ordered_mapping_value() {
let pairs = vec![
("first".to_string(), YamlValue::from("value1")),
("second".to_string(), YamlValue::from("value2")),
("third".to_string(), YamlValue::from("value3")),
];
let val = YamlValue::from_ordered_mapping(pairs);
assert!(val.is_ordered_mapping());
let yaml = val.to_yaml_string(0);
assert_eq!(
yaml,
"!!omap\n - first: value1\n - second: value2\n - third: value3"
);
let val = YamlValue::ordered_mapping();
assert_eq!(val.to_yaml_string(0), "!!omap []");
}
#[test]
fn test_pairs_value() {
let pairs = vec![
("key1".to_string(), YamlValue::from("value1")),
("key1".to_string(), YamlValue::from("value2")), ("key2".to_string(), YamlValue::from("value3")),
];
let val = YamlValue::from_pairs(pairs);
assert!(val.is_pairs());
let yaml = val.to_yaml_string(0);
assert_eq!(
yaml,
"!!pairs\n - key1: value1\n - key1: value2\n - key2: value3"
);
let val = YamlValue::pairs();
assert_eq!(val.to_yaml_string(0), "!!pairs []");
}
#[test]
fn test_as_str() {
let string_val = YamlValue::from("hello");
assert_eq!(string_val.as_str(), Some("hello"));
let int_val = YamlValue::from(42);
assert_eq!(int_val.as_str(), Some("42"));
let float_val = YamlValue::from(1.5);
assert_eq!(float_val.as_str(), Some("1.5"));
let bool_val = YamlValue::from(true);
assert_eq!(bool_val.as_str(), Some("true"));
let mapping = YamlValue::mapping();
assert_eq!(mapping.as_str(), None);
let sequence = YamlValue::sequence();
assert_eq!(sequence.as_str(), None);
let set = YamlValue::set();
assert_eq!(set.as_str(), None);
}
#[test]
fn test_to_i64() {
use crate::scalar::ScalarValue;
let int_val = YamlValue::Scalar(ScalarValue::parse("42"));
assert_eq!(int_val.to_i64(), Some(42));
let neg_int = YamlValue::Scalar(ScalarValue::parse("-123"));
assert_eq!(neg_int.to_i64(), Some(-123));
let hex_val = YamlValue::Scalar(ScalarValue::parse("0x2A"));
assert_eq!(hex_val.to_i64(), Some(42));
let bin_val = YamlValue::Scalar(ScalarValue::parse("0b101010"));
assert_eq!(bin_val.to_i64(), Some(42));
let oct_val = YamlValue::Scalar(ScalarValue::parse("0o52"));
assert_eq!(oct_val.to_i64(), Some(42));
let string_val = YamlValue::from("42");
assert_eq!(string_val.to_i64(), None);
let bool_val = YamlValue::Scalar(ScalarValue::parse("true"));
assert_eq!(bool_val.to_i64(), None);
let no_val = YamlValue::Scalar(ScalarValue::parse("no"));
assert_eq!(no_val.to_i64(), None);
let float_val = YamlValue::Scalar(ScalarValue::parse("3.14"));
assert_eq!(float_val.to_i64(), None);
let mapping = YamlValue::mapping();
assert_eq!(mapping.to_i64(), None);
let sequence = YamlValue::sequence();
assert_eq!(sequence.to_i64(), None);
let random_string = YamlValue::from("blah");
assert_eq!(random_string.to_i64(), None);
let large_int = YamlValue::Scalar(ScalarValue::parse("23432"));
assert_eq!(large_int.to_i64(), Some(23432));
let zero = YamlValue::Scalar(ScalarValue::parse("0"));
assert_eq!(zero.to_i64(), Some(0));
let one = YamlValue::Scalar(ScalarValue::parse("1"));
assert_eq!(one.to_i64(), Some(1));
let quoted_one = YamlValue::from("1");
assert_eq!(quoted_one.to_i64(), None);
let quoted_zero = YamlValue::from("0");
assert_eq!(quoted_zero.to_i64(), None);
}
#[test]
fn test_to_f64() {
use crate::scalar::ScalarValue;
let float_val = YamlValue::Scalar(ScalarValue::parse("1.5"));
assert_eq!(float_val.to_f64(), Some(1.5));
let neg_float = YamlValue::Scalar(ScalarValue::parse("-2.5"));
assert_eq!(neg_float.to_f64(), Some(-2.5));
let sci_val = YamlValue::Scalar(ScalarValue::parse("1.23e10"));
assert_eq!(sci_val.to_f64(), Some(1.23e10));
let string_val = YamlValue::from("3.14");
assert_eq!(string_val.to_f64(), None);
let int_val = YamlValue::Scalar(ScalarValue::parse("42"));
assert_eq!(int_val.to_f64(), None);
let bool_val = YamlValue::Scalar(ScalarValue::parse("true"));
assert_eq!(bool_val.to_f64(), None);
let mapping = YamlValue::mapping();
assert_eq!(mapping.to_f64(), None);
let sequence = YamlValue::sequence();
assert_eq!(sequence.to_f64(), None);
let random_string = YamlValue::from("blah");
assert_eq!(random_string.to_f64(), None);
}
#[test]
fn test_to_bool() {
use crate::scalar::ScalarValue;
let true_val = YamlValue::Scalar(ScalarValue::parse("true"));
assert_eq!(true_val.to_bool(), Some(true));
let false_val = YamlValue::Scalar(ScalarValue::parse("false"));
assert_eq!(false_val.to_bool(), Some(false));
let yes_val = YamlValue::Scalar(ScalarValue::parse("yes"));
assert_eq!(yes_val.to_bool(), Some(true));
let no_val = YamlValue::Scalar(ScalarValue::parse("no"));
assert_eq!(no_val.to_bool(), Some(false));
let on_val = YamlValue::Scalar(ScalarValue::parse("on"));
assert_eq!(on_val.to_bool(), Some(true));
let off_val = YamlValue::Scalar(ScalarValue::parse("off"));
assert_eq!(off_val.to_bool(), Some(false));
let true_upper = YamlValue::Scalar(ScalarValue::parse("TRUE"));
assert_eq!(true_upper.to_bool(), Some(true));
let yes_mixed = YamlValue::Scalar(ScalarValue::parse("Yes"));
assert_eq!(yes_mixed.to_bool(), Some(true));
let quoted_true = YamlValue::from("true");
assert_eq!(quoted_true.to_bool(), None);
let quoted_false = YamlValue::from("false");
assert_eq!(quoted_false.to_bool(), None);
let quoted_yes = YamlValue::from("yes");
assert_eq!(quoted_yes.to_bool(), None);
let quoted_no = YamlValue::from("no");
assert_eq!(quoted_no.to_bool(), None);
let zero = YamlValue::Scalar(ScalarValue::parse("0"));
assert_eq!(zero.to_bool(), None);
let one = YamlValue::Scalar(ScalarValue::parse("1"));
assert_eq!(one.to_bool(), None);
let large_int = YamlValue::Scalar(ScalarValue::parse("23432"));
assert_eq!(large_int.to_bool(), None);
let quoted_one = YamlValue::from("1");
assert_eq!(quoted_one.to_bool(), None);
let quoted_zero = YamlValue::from("0");
assert_eq!(quoted_zero.to_bool(), None);
let random_string = YamlValue::from("blah");
assert_eq!(random_string.to_bool(), None);
let mapping = YamlValue::mapping();
assert_eq!(mapping.to_bool(), None);
let sequence = YamlValue::sequence();
assert_eq!(sequence.to_bool(), None);
let float_val = YamlValue::Scalar(ScalarValue::parse("3.14"));
assert_eq!(float_val.to_bool(), None);
}
}
use crate::as_yaml::{AsYaml, YamlKind};
use crate::yaml::SyntaxNode;
impl AsYaml for YamlValue {
fn as_node(&self) -> Option<&SyntaxNode> {
None
}
fn kind(&self) -> YamlKind {
match self {
YamlValue::Scalar(_) => YamlKind::Scalar,
YamlValue::Sequence(_) => YamlKind::Sequence,
YamlValue::Mapping(_) => YamlKind::Mapping,
YamlValue::Set(_) => YamlKind::Tagged(std::borrow::Cow::Borrowed("!!set")),
YamlValue::OrderedMapping(_) => YamlKind::Tagged(std::borrow::Cow::Borrowed("!!omap")),
YamlValue::Pairs(_) => YamlKind::Tagged(std::borrow::Cow::Borrowed("!!pairs")),
}
}
fn build_content(
&self,
builder: &mut rowan::GreenNodeBuilder,
indent: usize,
flow_context: bool,
) -> bool {
use crate::lex::SyntaxKind;
match self {
YamlValue::Scalar(s) => {
builder.start_node(SyntaxKind::SCALAR.into());
let token_kind = if s.value().parse::<i64>().is_ok() {
SyntaxKind::INT
} else if s.value().parse::<f64>().is_ok() {
SyntaxKind::FLOAT
} else if s.value() == "true" || s.value() == "false" {
SyntaxKind::BOOL
} else if s.value() == "null" {
SyntaxKind::NULL
} else {
SyntaxKind::STRING
};
builder.token(token_kind.into(), s.value());
builder.finish_node();
false
}
YamlValue::Mapping(map) => map.build_content(builder, indent, flow_context),
YamlValue::Sequence(seq) => seq.as_slice().build_content(builder, indent, flow_context),
YamlValue::Set(set) => {
if set.is_empty() {
builder.start_node(SyntaxKind::TAGGED_NODE.into());
builder.token(SyntaxKind::TAG.into(), "!!set");
builder.token(SyntaxKind::WHITESPACE.into(), " ");
builder.start_node(SyntaxKind::MAPPING.into());
builder.token(SyntaxKind::LEFT_BRACE.into(), "{");
builder.token(SyntaxKind::RIGHT_BRACE.into(), "}");
builder.finish_node(); builder.finish_node(); return false;
}
builder.start_node(SyntaxKind::TAGGED_NODE.into());
builder.token(SyntaxKind::TAG.into(), "!!set");
builder.token(SyntaxKind::NEWLINE.into(), "\n");
builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent + 2));
let map: BTreeMap<String, YamlValue> = set
.iter()
.map(|k| (k.clone(), YamlValue::Scalar(ScalarValue::null())))
.collect();
let ends_with_newline = map.build_content(builder, indent + 2, false);
builder.finish_node(); ends_with_newline
}
YamlValue::OrderedMapping(omap) => {
builder.start_node(SyntaxKind::TAGGED_NODE.into());
builder.token(SyntaxKind::TAG.into(), "!!omap");
builder.token(SyntaxKind::NEWLINE.into(), "\n");
builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent + 2));
let seq: Vec<YamlValue> = omap
.iter()
.map(|(k, v)| {
let mut map = BTreeMap::new();
map.insert(k.clone(), v.clone());
YamlValue::Mapping(map)
})
.collect();
let ends_with_newline = seq.as_slice().build_content(builder, indent + 2, false);
builder.finish_node(); ends_with_newline
}
YamlValue::Pairs(pairs) => {
builder.start_node(SyntaxKind::TAGGED_NODE.into());
builder.token(SyntaxKind::TAG.into(), "!!pairs");
builder.token(SyntaxKind::NEWLINE.into(), "\n");
builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent + 2));
let seq: Vec<YamlValue> = pairs
.iter()
.map(|(k, v)| {
let mut map = BTreeMap::new();
map.insert(k.clone(), v.clone());
YamlValue::Mapping(map)
})
.collect();
let ends_with_newline = seq.as_slice().build_content(builder, indent + 2, false);
builder.finish_node(); ends_with_newline
}
}
}
fn is_inline(&self) -> bool {
match self {
YamlValue::Scalar(_) => true,
YamlValue::Mapping(map) => map.is_empty(),
YamlValue::Sequence(seq) => seq.is_empty(),
YamlValue::Set(_) => true,
YamlValue::OrderedMapping(_) => true,
YamlValue::Pairs(_) => true,
}
}
}
#[derive(Debug, Clone, Copy)]
enum CollectionStyle {
Flow,
Block,
}
impl AsYaml for BTreeMap<String, YamlValue> {
fn as_node(&self) -> Option<&SyntaxNode> {
None
}
fn kind(&self) -> YamlKind {
YamlKind::Mapping
}
fn build_content(
&self,
builder: &mut rowan::GreenNodeBuilder,
indent: usize,
_flow_context: bool,
) -> bool {
use crate::lex::SyntaxKind;
let style = if self.is_empty() {
CollectionStyle::Flow
} else {
CollectionStyle::Block
};
builder.start_node(SyntaxKind::MAPPING.into());
match style {
CollectionStyle::Flow => {
builder.token(SyntaxKind::LEFT_BRACE.into(), "{");
builder.token(SyntaxKind::RIGHT_BRACE.into(), "}");
}
CollectionStyle::Block => {
let indent_str = " ".repeat(indent);
let entries: Vec<_> = self.iter().collect();
for (i, (key, value)) in entries.iter().enumerate() {
if i > 0 {
builder.token(SyntaxKind::INDENT.into(), &indent_str);
}
builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
builder.start_node(SyntaxKind::KEY.into());
key.as_str().build_content(builder, 0, false);
builder.finish_node();
builder.token(SyntaxKind::COLON.into(), ":");
builder.start_node(SyntaxKind::VALUE.into());
let is_complex =
value.kind() == YamlKind::Mapping || value.kind() == YamlKind::Sequence;
let value_ends_with_newline = if is_complex {
builder.token(SyntaxKind::NEWLINE.into(), "\n");
builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent + 2));
value.build_content(builder, indent + 2, _flow_context)
} else {
builder.token(SyntaxKind::WHITESPACE.into(), " ");
value.build_content(builder, 0, _flow_context)
};
builder.finish_node();
if !value_ends_with_newline {
builder.token(SyntaxKind::NEWLINE.into(), "\n");
}
builder.finish_node(); }
}
}
builder.finish_node(); matches!(style, CollectionStyle::Block) && !self.is_empty()
}
fn is_inline(&self) -> bool {
self.is_empty()
}
}
impl AsYaml for Vec<YamlValue> {
fn as_node(&self) -> Option<&SyntaxNode> {
None
}
fn kind(&self) -> YamlKind {
YamlKind::Sequence
}
fn build_content(
&self,
builder: &mut rowan::GreenNodeBuilder,
indent: usize,
flow_context: bool,
) -> bool {
<&[YamlValue] as AsYaml>::build_content(&self.as_slice(), builder, indent, flow_context)
}
fn is_inline(&self) -> bool {
<&[YamlValue] as AsYaml>::is_inline(&self.as_slice())
}
}
impl AsYaml for &[YamlValue] {
fn as_node(&self) -> Option<&SyntaxNode> {
None
}
fn kind(&self) -> YamlKind {
YamlKind::Sequence
}
fn build_content(
&self,
builder: &mut rowan::GreenNodeBuilder,
indent: usize,
_flow_context: bool,
) -> bool {
use crate::lex::SyntaxKind;
let style = if self.is_empty() {
CollectionStyle::Flow
} else {
CollectionStyle::Block
};
builder.start_node(SyntaxKind::SEQUENCE.into());
match style {
CollectionStyle::Flow => {
builder.token(SyntaxKind::LEFT_BRACKET.into(), "[");
builder.token(SyntaxKind::RIGHT_BRACKET.into(), "]");
}
CollectionStyle::Block => {
let indent_str = " ".repeat(indent);
for (i, item) in self.iter().enumerate() {
if i > 0 {
builder.token(SyntaxKind::INDENT.into(), &indent_str);
}
builder.start_node(SyntaxKind::SEQUENCE_ENTRY.into());
builder.token(SyntaxKind::DASH.into(), "-");
builder.token(SyntaxKind::WHITESPACE.into(), " ");
let item_ends_with_newline =
item.build_content(builder, indent + 2, _flow_context);
if !item_ends_with_newline {
builder.token(SyntaxKind::NEWLINE.into(), "\n");
}
builder.finish_node(); }
}
}
builder.finish_node(); matches!(style, CollectionStyle::Block) && !self.is_empty()
}
fn is_inline(&self) -> bool {
self.is_empty()
}
}