robinpath_modules/modules/
yaml_mod.rs1use robinpath::{RobinPath, Value};
2use crate::serde_convert::value_to_json;
3
4pub fn register(rp: &mut RobinPath) {
5 rp.register_builtin("yaml.parse", |args, _| {
6 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
7 Ok(parse_yaml(&s))
8 });
9
10 rp.register_builtin("yaml.stringify", |args, _| {
11 let val = args.first().cloned().unwrap_or(Value::Null);
12 let _indent = args.get(1).map(|v| v.to_number() as usize).unwrap_or(2);
13 Ok(Value::String(stringify_yaml(&val)))
14 });
15
16 rp.register_builtin("yaml.parseFile", |args, _| {
17 let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
18 match std::fs::read_to_string(&path) {
19 Ok(content) => Ok(parse_yaml(&content)),
20 Err(e) => Err(format!("yaml.parseFile error: {}", e)),
21 }
22 });
23
24 rp.register_builtin("yaml.writeFile", |args, _| {
25 let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
26 let val = args.get(1).cloned().unwrap_or(Value::Null);
27 let yaml_str = stringify_yaml(&val);
28 match std::fs::write(&path, &yaml_str) {
29 Ok(()) => Ok(Value::Bool(true)),
30 Err(e) => Err(format!("yaml.writeFile error: {}", e)),
31 }
32 });
33
34 rp.register_builtin("yaml.isValid", |args, _| {
35 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
36 let trimmed = s.trim();
37 if trimmed.is_empty() {
38 return Ok(Value::Bool(false));
39 }
40 let is_valid = serde_yaml::from_str::<serde_yaml::Value>(trimmed).is_ok();
42 Ok(Value::Bool(is_valid))
43 });
44
45 rp.register_builtin("yaml.get", |args, _| {
46 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
47 let path = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
48 let parsed = parse_yaml(&s);
49 Ok(get_by_path(&parsed, &path))
50 });
51
52 rp.register_builtin("yaml.toJSON", |args, _| {
53 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
54 let parsed = parse_yaml(&s);
55 let json_val: serde_json::Value = parsed.into();
56 Ok(Value::String(serde_json::to_string_pretty(&json_val).unwrap_or_default()))
57 });
58
59 rp.register_builtin("yaml.fromJSON", |args, _| {
60 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
61 match serde_json::from_str::<serde_json::Value>(&s) {
62 Ok(v) => {
63 let val = Value::from(v);
64 Ok(Value::String(stringify_yaml(&val)))
65 }
66 Err(e) => Err(format!("yaml.fromJSON error: {}", e)),
67 }
68 });
69
70 rp.register_builtin("yaml.parseAll", |args, _| {
71 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
72 let docs: Vec<Value> = serde_yaml::Deserializer::from_str(&s)
73 .map(|doc| {
74 match serde_yaml::Value::deserialize(doc) {
75 Ok(v) => yaml_value_to_value(v),
76 Err(_) => Value::Null,
77 }
78 })
79 .collect();
80 Ok(Value::Array(docs))
81 });
82}
83
84use serde::Deserialize;
85
86fn parse_yaml(s: &str) -> Value {
87 let trimmed = s.trim();
88 if trimmed.is_empty() {
89 return Value::Null;
90 }
91
92 match serde_yaml::from_str::<serde_yaml::Value>(trimmed) {
93 Ok(v) => yaml_value_to_value(v),
94 Err(_) => Value::Null,
95 }
96}
97
98fn yaml_value_to_value(v: serde_yaml::Value) -> Value {
99 match v {
100 serde_yaml::Value::Null => Value::Null,
101 serde_yaml::Value::Bool(b) => Value::Bool(b),
102 serde_yaml::Value::Number(n) => {
103 if let Some(i) = n.as_i64() {
104 Value::Number(i as f64)
105 } else if let Some(f) = n.as_f64() {
106 Value::Number(f)
107 } else {
108 Value::Number(0.0)
109 }
110 }
111 serde_yaml::Value::String(s) => Value::String(s),
112 serde_yaml::Value::Sequence(arr) => {
113 Value::Array(arr.into_iter().map(yaml_value_to_value).collect())
114 }
115 serde_yaml::Value::Mapping(map) => {
116 let mut obj = indexmap::IndexMap::new();
117 for (k, v) in map {
118 let key = match k {
119 serde_yaml::Value::String(s) => s,
120 serde_yaml::Value::Number(n) => n.to_string(),
121 serde_yaml::Value::Bool(b) => b.to_string(),
122 _ => continue,
123 };
124 obj.insert(key, yaml_value_to_value(v));
125 }
126 Value::Object(obj)
127 }
128 serde_yaml::Value::Tagged(tagged) => yaml_value_to_value(tagged.value),
129 }
130}
131
132fn stringify_yaml(val: &Value) -> String {
133 let json = value_to_json(val);
134 match serde_yaml::to_string(&json) {
136 Ok(s) => s.trim_end().to_string(),
137 Err(_) => String::new(),
138 }
139}
140
141fn get_by_path(val: &Value, path: &str) -> Value {
142 let parts: Vec<&str> = path.split('.').collect();
143 let mut current = val.clone();
144 for part in parts {
145 if let Value::Object(obj) = ¤t {
146 current = obj.get(part).cloned().unwrap_or(Value::Null);
147 } else {
148 return Value::Null;
149 }
150 }
151 current
152}