use noyalib::Value;
use noyalib::cst::Document;
pub fn parse_yaml_to_value(yaml: &str) -> noyalib::Result<Value> {
noyalib::from_str(yaml)
}
pub fn value_to_yaml(value: &Value) -> noyalib::Result<String> {
noyalib::to_string(value)
}
pub fn yaml_round_trip(yaml: &str) -> noyalib::Result<String> {
let v = parse_yaml_to_value(yaml)?;
value_to_yaml(&v)
}
pub fn validate_yaml_json(yaml: &str) -> noyalib::Result<bool> {
let value: Value = noyalib::from_str(yaml)?;
noyalib::validate_yaml_json_schema(&value).map(|()| true)
}
pub fn yaml_get_path(yaml: &str, path: &str) -> noyalib::Result<Option<Value>> {
let value: Value = noyalib::from_str(yaml)?;
Ok(value.get_path(path).cloned())
}
pub fn merge_yaml(base_yaml: &str, override_yaml: &str) -> noyalib::Result<String> {
let mut base: Value = noyalib::from_str(base_yaml)?;
let overrides: Value = noyalib::from_str(override_yaml)?;
base.merge(overrides);
noyalib::to_string(&base)
}
pub fn document_span_at(doc: &Document, path: &str) -> Option<(usize, usize)> {
doc.span_at(path)
}
pub fn document_get_value(doc: &Document, path: &str) -> Option<Value> {
doc.as_value().get_path(path).cloned()
}
pub fn document_get_source<'a>(doc: &'a Document, path: &str) -> Option<&'a str> {
doc.get(path)
}
pub fn document_comments_at(doc: &Document, path: &str) -> (Vec<String>, Option<String>) {
let bundle = doc.comments_at(path);
let before: Vec<String> = bundle.before.iter().map(|c| c.text.clone()).collect();
let inline: Option<String> = bundle.inline.map(|c| c.text);
(before, inline)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_yaml_to_value_basic() {
let v = parse_yaml_to_value("a: 1\nb: hello\n").unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
assert_eq!(v["b"].as_str(), Some("hello"));
}
#[test]
fn parse_yaml_to_value_returns_parse_error() {
let res = parse_yaml_to_value("a: [\n");
assert!(res.is_err(), "unterminated flow sequence must error");
}
#[test]
fn value_to_yaml_round_trips() {
let yaml = "a: 1\nb: 2\n";
let v = parse_yaml_to_value(yaml).unwrap();
let out = value_to_yaml(&v).unwrap();
let v2 = parse_yaml_to_value(&out).unwrap();
assert_eq!(v, v2);
}
#[test]
fn yaml_round_trip_smoke() {
let out = yaml_round_trip("k: 42\n").unwrap();
assert!(out.contains("k:"));
assert!(out.contains("42"));
}
#[test]
fn validate_yaml_json_succeeds_on_json_compatible_input() {
let ok = validate_yaml_json("a: 1\nb: hello\n").unwrap();
assert!(ok);
}
#[test]
fn validate_yaml_json_rejects_unparseable() {
let res = validate_yaml_json("a: [\n");
assert!(res.is_err());
}
#[test]
fn yaml_get_path_finds_existing_key() {
let v = yaml_get_path("a:\n b: 42\n", "a.b").unwrap();
assert_eq!(v.unwrap().as_i64(), Some(42));
}
#[test]
fn yaml_get_path_returns_none_for_missing_key() {
let v = yaml_get_path("a: 1\n", "missing.path").unwrap();
assert!(v.is_none());
}
#[test]
fn yaml_get_path_propagates_parse_error() {
let res = yaml_get_path("a: [\n", "a");
assert!(res.is_err());
}
#[test]
fn merge_yaml_combines_disjoint_keys() {
let merged = merge_yaml("a: 1\n", "b: 2\n").unwrap();
let v = parse_yaml_to_value(&merged).unwrap();
assert_eq!(v["a"].as_i64(), Some(1));
assert_eq!(v["b"].as_i64(), Some(2));
}
#[test]
fn merge_yaml_override_wins_on_conflict() {
let merged = merge_yaml("port: 80\n", "port: 8080\n").unwrap();
let v = parse_yaml_to_value(&merged).unwrap();
assert_eq!(v["port"].as_i64(), Some(8080));
}
#[test]
fn merge_yaml_propagates_base_parse_error() {
let res = merge_yaml("a: [\n", "b: 1\n");
assert!(res.is_err());
}
#[test]
fn merge_yaml_propagates_override_parse_error() {
let res = merge_yaml("a: 1\n", "b: [\n");
assert!(res.is_err());
}
#[test]
fn document_span_at_returns_value_range() {
let doc = noyalib::cst::parse_document("name: noyalib\n").unwrap();
let (start, end) = document_span_at(&doc, "name").unwrap();
assert!(start < end);
assert_eq!(&"name: noyalib\n"[start..end], "noyalib");
}
#[test]
fn document_span_at_missing_path_is_none() {
let doc = noyalib::cst::parse_document("a: 1\n").unwrap();
assert!(document_span_at(&doc, "missing").is_none());
}
#[test]
fn document_get_value_resolves_nested_path() {
let doc = noyalib::cst::parse_document("a:\n b: 42\n").unwrap();
let v = document_get_value(&doc, "a.b").unwrap();
assert_eq!(v.as_i64(), Some(42));
}
#[test]
fn document_get_value_missing_returns_none() {
let doc = noyalib::cst::parse_document("a: 1\n").unwrap();
assert!(document_get_value(&doc, "x.y").is_none());
}
#[test]
fn document_get_source_returns_raw_fragment() {
let doc = noyalib::cst::parse_document("name: noyalib\n").unwrap();
let s = document_get_source(&doc, "name").unwrap();
assert_eq!(s, "noyalib");
}
#[test]
fn document_get_source_missing_is_none() {
let doc = noyalib::cst::parse_document("a: 1\n").unwrap();
assert!(document_get_source(&doc, "missing").is_none());
}
#[test]
fn document_comments_at_returns_before_and_inline() {
let yaml = "# top-level\nname: noyalib # inline\n";
let doc = noyalib::cst::parse_document(yaml).unwrap();
let (before, inline) = document_comments_at(&doc, "name");
assert_eq!(before.len(), 1);
assert!(before[0].contains("top-level"));
assert!(inline.is_some());
assert!(inline.unwrap().contains("inline"));
}
#[test]
fn document_comments_at_no_comments_returns_empty() {
let doc = noyalib::cst::parse_document("name: noyalib\n").unwrap();
let (before, inline) = document_comments_at(&doc, "name");
assert!(before.is_empty());
assert!(inline.is_none());
}
}