use super::{TaggedValue, Value};
use crate::error::Result;
use crate::node::NodeType;
use crate::scalar_parse;
use crate::NodeRef;
use indexmap::IndexMap;
impl Value {
pub fn from_node_ref(node: NodeRef<'_>) -> Result<Value> {
Self::from_node_ref_inner(node)
}
fn from_node_ref_inner(node: NodeRef<'_>) -> Result<Value> {
let tag = node.tag_str()?;
let value = match node.kind() {
NodeType::Scalar => {
let raw = node.scalar_str()?;
if node.is_non_plain() {
Value::String(raw.to_string())
} else {
infer_scalar_type(raw)
}
}
NodeType::Sequence => {
let len = node.seq_len().unwrap_or(0);
let mut items = Vec::with_capacity(len);
for item in node.seq_iter() {
items.push(Self::from_node_ref_inner(item)?);
}
Value::Sequence(items)
}
NodeType::Mapping => {
let len = node.map_len().unwrap_or(0);
let mut map = IndexMap::with_capacity(len);
for (key_node, value_node) in node.map_iter() {
let key = Self::from_node_ref_inner(key_node)?;
let value = Self::from_node_ref_inner(value_node)?;
map.insert(key, value);
}
Value::Mapping(map)
}
};
match tag {
Some(t) => Ok(Value::Tagged(Box::new(TaggedValue {
tag: t.to_string(),
value,
}))),
None => Ok(value),
}
}
}
fn infer_scalar_type(s: &str) -> Value {
if scalar_parse::is_null(s) {
return Value::Null;
}
if let Some(b) = scalar_parse::parse_bool(s) {
return Value::Bool(b);
}
if let Some(n) = scalar_parse::parse_number(s) {
return Value::Number(n);
}
Value::String(s.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::Number;
use crate::Document;
#[test]
fn test_infer_null() {
assert_eq!(infer_scalar_type(""), Value::Null);
assert_eq!(infer_scalar_type("~"), Value::Null);
assert_eq!(infer_scalar_type("null"), Value::Null);
assert_eq!(infer_scalar_type("NULL"), Value::Null);
}
#[test]
fn test_infer_bool() {
assert_eq!(infer_scalar_type("true"), Value::Bool(true));
assert_eq!(infer_scalar_type("True"), Value::Bool(true));
assert_eq!(infer_scalar_type("yes"), Value::Bool(true));
assert_eq!(infer_scalar_type("false"), Value::Bool(false));
assert_eq!(infer_scalar_type("False"), Value::Bool(false));
assert_eq!(infer_scalar_type("no"), Value::Bool(false));
}
#[test]
fn test_infer_integer() {
assert_eq!(infer_scalar_type("42"), Value::Number(Number::UInt(42)));
assert_eq!(infer_scalar_type("-42"), Value::Number(Number::Int(-42)));
assert_eq!(infer_scalar_type("0xFF"), Value::Number(Number::UInt(255)));
assert_eq!(infer_scalar_type("0o77"), Value::Number(Number::UInt(63)));
}
#[test]
fn test_infer_float() {
assert_eq!(infer_scalar_type("2.5"), Value::Number(Number::Float(2.5)));
assert_eq!(
infer_scalar_type("1.0e10"),
Value::Number(Number::Float(1.0e10))
);
}
#[test]
fn test_infer_special_floats() {
match infer_scalar_type(".inf") {
Value::Number(Number::Float(f)) => assert!(f.is_infinite() && f.is_sign_positive()),
_ => panic!("Expected positive infinity"),
}
match infer_scalar_type("-.inf") {
Value::Number(Number::Float(f)) => assert!(f.is_infinite() && f.is_sign_negative()),
_ => panic!("Expected negative infinity"),
}
match infer_scalar_type(".nan") {
Value::Number(Number::Float(f)) => assert!(f.is_nan()),
_ => panic!("Expected NaN"),
}
}
#[test]
fn test_infer_string() {
assert_eq!(infer_scalar_type("hello"), Value::String("hello".into()));
assert_eq!(
infer_scalar_type("hello world"),
Value::String("hello world".into())
);
}
#[test]
fn test_from_node_ref_scalar() {
let doc = Document::parse_str("42").unwrap();
let root = doc.root().unwrap();
let value = Value::from_node_ref(root).unwrap();
assert_eq!(value, Value::Number(Number::UInt(42)));
}
#[test]
fn test_from_node_ref_sequence() {
let doc = Document::parse_str("[1, 2, 3]").unwrap();
let root = doc.root().unwrap();
let value = Value::from_node_ref(root).unwrap();
assert!(value.is_sequence());
let seq = value.as_sequence().unwrap();
assert_eq!(seq.len(), 3);
}
#[test]
fn test_from_node_ref_mapping() {
let doc = Document::parse_str("foo: bar").unwrap();
let root = doc.root().unwrap();
let value = Value::from_node_ref(root).unwrap();
assert!(value.is_mapping());
assert_eq!(value["foo"], Value::String("bar".into()));
}
#[test]
fn test_from_node_ref_nested() {
let doc = Document::parse_str("users:\n - name: Alice\n - name: Bob").unwrap();
let root = doc.root().unwrap();
let value = Value::from_node_ref(root).unwrap();
assert!(value.is_mapping());
let users = value["users"].as_sequence().unwrap();
assert_eq!(users.len(), 2);
assert_eq!(users[0]["name"], Value::String("Alice".into()));
assert_eq!(users[1]["name"], Value::String("Bob".into()));
}
#[test]
fn test_from_node_ref_quoted_string() {
let doc = Document::parse_str("quoted: 'true'").unwrap();
let root = doc.root().unwrap();
let value = Value::from_node_ref(root).unwrap();
assert_eq!(value["quoted"], Value::String("true".into()));
}
#[test]
fn test_from_node_ref_type_inference() {
let doc = Document::parse_str("bool: true\nnum: 42\nfloat: 2.5\nnull: ~").unwrap();
let root = doc.root().unwrap();
let value = Value::from_node_ref(root).unwrap();
assert_eq!(value["bool"], Value::Bool(true));
assert_eq!(value["num"], Value::Number(Number::UInt(42)));
assert_eq!(value["float"], Value::Number(Number::Float(2.5)));
assert_eq!(value["null"], Value::Null);
}
#[test]
fn test_value_parse() {
let value: Value = "key: value".parse().unwrap();
assert!(value.is_mapping());
assert_eq!(value["key"], Value::String("value".into()));
}
}