robinpath_modules/modules/
toml_mod.rs1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4 rp.register_builtin("toml.parse", |args, _| {
5 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
6 Ok(parse_toml(&s))
7 });
8
9 rp.register_builtin("toml.stringify", |args, _| {
10 let val = args.first().cloned().unwrap_or(Value::Null);
11 Ok(Value::String(stringify_toml(&val)))
12 });
13
14 rp.register_builtin("toml.parseFile", |args, _| {
15 let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
16 match std::fs::read_to_string(&path) {
17 Ok(content) => Ok(parse_toml(&content)),
18 Err(e) => Err(format!("toml.parseFile error: {}", e)),
19 }
20 });
21
22 rp.register_builtin("toml.writeFile", |args, _| {
23 let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
24 let val = args.get(1).cloned().unwrap_or(Value::Null);
25 let toml_str = stringify_toml(&val);
26 match std::fs::write(&path, &toml_str) {
27 Ok(()) => Ok(Value::Bool(true)),
28 Err(e) => Err(format!("toml.writeFile error: {}", e)),
29 }
30 });
31
32 rp.register_builtin("toml.get", |args, _| {
33 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
34 let path = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
35 let parsed = parse_toml(&s);
36 Ok(get_by_path(&parsed, &path))
37 });
38
39 rp.register_builtin("toml.isValid", |args, _| {
40 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
41 let is_valid = s.parse::<toml_crate::Table>().is_ok();
42 Ok(Value::Bool(is_valid))
43 });
44
45 rp.register_builtin("toml.toJSON", |args, _| {
46 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
47 let parsed = parse_toml(&s);
48 let json_val: serde_json::Value = parsed.into();
49 Ok(Value::String(serde_json::to_string_pretty(&json_val).unwrap_or_default()))
50 });
51
52 rp.register_builtin("toml.fromJSON", |args, _| {
53 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
54 match serde_json::from_str::<serde_json::Value>(&s) {
55 Ok(v) => Ok(Value::String(stringify_toml(&Value::from(v)))),
56 Err(e) => Err(format!("toml.fromJSON error: {}", e)),
57 }
58 });
59}
60
61fn parse_toml(s: &str) -> Value {
62 let trimmed = s.trim();
63 if trimmed.is_empty() {
64 return Value::Null;
65 }
66 match trimmed.parse::<toml_crate::Value>() {
67 Ok(v) => toml_value_to_value(v),
68 Err(_) => Value::Null,
69 }
70}
71
72fn toml_value_to_value(v: toml_crate::Value) -> Value {
73 match v {
74 toml_crate::Value::String(s) => Value::String(s),
75 toml_crate::Value::Integer(i) => Value::Number(i as f64),
76 toml_crate::Value::Float(f) => Value::Number(f),
77 toml_crate::Value::Boolean(b) => Value::Bool(b),
78 toml_crate::Value::Datetime(dt) => Value::String(dt.to_string()),
79 toml_crate::Value::Array(arr) => {
80 Value::Array(arr.into_iter().map(toml_value_to_value).collect())
81 }
82 toml_crate::Value::Table(table) => {
83 let mut obj = indexmap::IndexMap::new();
84 for (k, v) in table {
85 obj.insert(k, toml_value_to_value(v));
86 }
87 Value::Object(obj)
88 }
89 }
90}
91
92fn value_to_toml(val: &Value) -> toml_crate::Value {
93 match val {
94 Value::String(s) => toml_crate::Value::String(s.clone()),
95 Value::Number(n) => {
96 if *n == (*n as i64) as f64 {
97 toml_crate::Value::Integer(*n as i64)
98 } else {
99 toml_crate::Value::Float(*n)
100 }
101 }
102 Value::Bool(b) => toml_crate::Value::Boolean(*b),
103 Value::Array(arr) => {
104 toml_crate::Value::Array(arr.iter().map(value_to_toml).collect())
105 }
106 Value::Object(obj) => {
107 let mut table = toml_crate::map::Map::new();
108 for (k, v) in obj {
109 table.insert(k.clone(), value_to_toml(v));
110 }
111 toml_crate::Value::Table(table)
112 }
113 Value::Null => toml_crate::Value::String(String::new()),
114 _ => toml_crate::Value::String(val.to_display_string()),
115 }
116}
117
118fn stringify_toml(val: &Value) -> String {
119 let toml_val = value_to_toml(val);
120 match toml_crate::to_string_pretty(&toml_val) {
121 Ok(s) => s.trim_end().to_string(),
122 Err(_) => String::new(),
123 }
124}
125
126fn get_by_path(val: &Value, path: &str) -> Value {
127 let parts: Vec<&str> = path.split('.').collect();
128 let mut current = val.clone();
129 for part in parts {
130 if let Value::Object(obj) = ¤t {
131 current = obj.get(part).cloned().unwrap_or(Value::Null);
132 } else {
133 return Value::Null;
134 }
135 }
136 current
137}