Skip to main content

robinpath_modules/modules/
toml_mod.rs

1use 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) = &current {
131            current = obj.get(part).cloned().unwrap_or(Value::Null);
132        } else {
133            return Value::Null;
134        }
135    }
136    current
137}