Skip to main content

tatara_lisp_script/stdlib/
yaml.rs

1//! YAML parse → tatara-lisp `Value` tree. Shape matches the JSON mapping
2//! (objects → alists of 2-lists), so `alist-get` works identically for
3//! YAML and JSON documents.
4//!
5//!   (yaml-parse STR) → nested Value
6//!   (yaml-read PATH) → same but from a file path
7
8use std::sync::Arc;
9
10use tatara_lisp_eval::{Arity, EvalError, Interpreter, Value};
11
12use crate::script_ctx::ScriptCtx;
13use crate::stdlib::env::str_arg;
14
15pub fn install(interp: &mut Interpreter<ScriptCtx>) {
16    interp.register_fn(
17        "yaml-parse",
18        Arity::Exact(1),
19        |args: &[Value], _ctx: &mut ScriptCtx, sp| {
20            let s = str_arg(&args[0], "yaml-parse", sp)?;
21            let parsed: serde_yaml::Value = serde_yaml::from_str(&s)
22                .map_err(|e| EvalError::native_fn("yaml-parse", e.to_string(), sp))?;
23            Ok(yaml_to_value(&parsed))
24        },
25    );
26
27    interp.register_fn(
28        "yaml-read",
29        Arity::Exact(1),
30        |args: &[Value], _ctx: &mut ScriptCtx, sp| {
31            let path = str_arg(&args[0], "yaml-read", sp)?;
32            let body = std::fs::read_to_string(&*path)
33                .map_err(|e| EvalError::native_fn("yaml-read", format!("{path}: {e}"), sp))?;
34            let parsed: serde_yaml::Value = serde_yaml::from_str(&body)
35                .map_err(|e| EvalError::native_fn("yaml-read", format!("{path}: {e}"), sp))?;
36            Ok(yaml_to_value(&parsed))
37        },
38    );
39}
40
41fn yaml_to_value(y: &serde_yaml::Value) -> Value {
42    match y {
43        serde_yaml::Value::Null => Value::Nil,
44        serde_yaml::Value::Bool(b) => Value::Bool(*b),
45        serde_yaml::Value::Number(n) => {
46            if let Some(i) = n.as_i64() {
47                Value::Int(i)
48            } else {
49                Value::Float(n.as_f64().unwrap_or(0.0))
50            }
51        }
52        serde_yaml::Value::String(s) => Value::Str(Arc::from(s.as_str())),
53        serde_yaml::Value::Sequence(xs) => {
54            Value::list(xs.iter().map(yaml_to_value).collect::<Vec<_>>())
55        }
56        serde_yaml::Value::Mapping(m) => Value::list(
57            m.iter()
58                .map(|(k, v)| {
59                    let key_str = match k {
60                        serde_yaml::Value::String(s) => s.clone(),
61                        other => serde_yaml::to_string(other)
62                            .unwrap_or_default()
63                            .trim()
64                            .to_string(),
65                    };
66                    Value::list(vec![Value::Str(Arc::from(key_str)), yaml_to_value(v)])
67                })
68                .collect::<Vec<_>>(),
69        ),
70        serde_yaml::Value::Tagged(t) => yaml_to_value(&t.value),
71    }
72}