Skip to main content

mumujson/
lib.rs

1// LOCAL/src/lib.rs
2// FILE: json-mumu/src/lib.rs
3
4use std::ffi::c_void;
5
6use indexmap::IndexMap;
7use mumu::parser::interpreter::Interpreter;
8use mumu::parser::types::Value;
9use serde_json::{Map as JsonMap, Number as JsonNumber, Value as JsonVal};
10
11mod codec;     // json:encode / json:decode
12mod validate;  // json:validate  (returns Bool)
13mod report;    // json:report    (returns Bool | StrArray)
14
15// Host (native) builds: real Rust jsonschema support (schema.rs present only for host)
16#[cfg(not(target_arch = "wasm32"))]
17mod schema;
18
19/* ───────────────────────── Public registration (static) ────────────────── */
20/// Register all `json:*` bridges into the provided interpreter.
21/// On WASM we intentionally skip json:schema to avoid pulling jsonschema/getrandom.
22pub fn register_all(interpreter: &mut Interpreter) {
23    codec::register_json_decode(interpreter);
24    codec::register_json_encode(interpreter);
25    validate::register_json_validate(interpreter);
26    report::register_json_report(interpreter);
27
28    #[cfg(not(target_arch = "wasm32"))]
29    {
30        schema::register_json_schema(interpreter);
31    }
32}
33
34/* ───────────── Host/dynamic loader entrypoint (extend("json")) ─────────── */
35#[cfg(not(target_arch = "wasm32"))]
36#[no_mangle]
37pub unsafe extern "C" fn Cargo_lock(
38    interp_ptr: *mut c_void,
39    extra_str: *const c_void,
40) -> i32 {
41    use std::ffi::CStr;
42
43    if interp_ptr.is_null() {
44        eprintln!("[json plugin] null interpreter pointer");
45        return 1;
46    }
47    let interpreter = &mut *(interp_ptr as *mut Interpreter);
48
49    if interpreter.is_verbose() {
50        if !extra_str.is_null() {
51            let extra = CStr::from_ptr(extra_str as *const i8).to_string_lossy();
52            eprintln!("[json plugin] loading (extra = \"{extra}\")");
53        } else {
54            eprintln!("[json plugin] loading");
55        }
56    }
57
58    register_all(interpreter);
59
60    if interpreter.is_verbose() {
61        eprintln!("[json plugin] ready");
62    }
63    0
64}
65
66/* ═══════════════════ shared helpers (used across modules) ═══════════════ */
67/// serde_json → mumu::Value
68pub fn jsonval_to_mumu(jv: &JsonVal) -> Value {
69    match jv {
70        JsonVal::Null => Value::Placeholder,
71        JsonVal::Bool(b) => Value::Bool(*b),
72        JsonVal::Number(n) => {
73            if let Some(i) = n.as_i64() {
74                if (i32::MIN as i64..=i32::MAX as i64).contains(&i) {
75                    Value::Int(i as i32)
76                } else {
77                    Value::Long(i)
78                }
79            } else if let Some(f) = n.as_f64() {
80                Value::Float(f)
81            } else {
82                Value::Placeholder
83            }
84        }
85        JsonVal::String(s) => Value::SingleString(s.clone()),
86        JsonVal::Array(arr) => {
87            if arr.iter().all(|v| v.is_i64()) {
88                Value::IntArray(arr.iter().map(|v| v.as_i64().unwrap() as i32).collect())
89            } else if arr.iter().all(|v| v.is_f64()) {
90                Value::FloatArray(arr.iter().map(|v| v.as_f64().unwrap()).collect())
91            } else if arr.iter().all(|v| v.is_string()) {
92                Value::StrArray(arr.iter().map(|v| v.as_str().unwrap().to_string()).collect())
93            } else {
94                let mut items = Vec::with_capacity(arr.len());
95                for sub in arr {
96                    items.push(jsonval_to_mumu(sub));
97                }
98                Value::MixedArray(items)
99            }
100        }
101        JsonVal::Object(obj) => {
102            let mut map = IndexMap::new();
103            for (k, v) in obj {
104                map.insert(k.clone(), jsonval_to_mumu(v));
105            }
106            Value::KeyedArray(map)
107        }
108    }
109}
110
111/// mumu::Value → serde_json
112pub fn mumu_value_to_json(v: &Value) -> JsonVal {
113    match v {
114        Value::Placeholder => JsonVal::Null,
115        Value::Bool(b) => JsonVal::Bool(*b),
116        Value::Int(i) => JsonVal::Number((*i).into()),
117        Value::Long(l) => JsonVal::Number((*l).into()),
118        Value::Float(f) => {
119            if let Some(n) = JsonNumber::from_f64(*f) {
120                JsonVal::Number(n)
121            } else {
122                JsonVal::Null
123            }
124        }
125        Value::SingleString(s) => JsonVal::String(s.clone()),
126        Value::IntArray(xs) => {
127            JsonVal::Array(xs.iter().map(|n| JsonVal::Number((*n).into())).collect())
128        }
129        Value::FloatArray(fs) => {
130            JsonVal::Array(
131                fs.iter()
132                    .map(|f| {
133                        if let Some(n) = JsonNumber::from_f64(*f) {
134                            JsonVal::Number(n)
135                        } else {
136                            JsonVal::Null
137                        }
138                    })
139                    .collect(),
140            )
141        }
142        Value::BoolArray(bs) => JsonVal::Array(bs.iter().map(|b| JsonVal::Bool(*b)).collect()),
143        Value::StrArray(ss) => {
144            JsonVal::Array(ss.iter().map(|s| JsonVal::String(s.clone())).collect())
145        }
146        Value::Int2DArray(rows) => JsonVal::Array(
147            rows.iter()
148                .map(|r| {
149                    JsonVal::Array(
150                        r.iter()
151                            .map(|n| JsonVal::Number((*n).into()))
152                            .collect::<Vec<_>>(),
153                    )
154                })
155                .collect(),
156        ),
157        Value::Float2DArray(rows) => JsonVal::Array(
158            rows.iter()
159                .map(|r| {
160                    JsonVal::Array(
161                        r.iter()
162                            .map(|f| {
163                                if let Some(n) = JsonNumber::from_f64(*f) {
164                                    JsonVal::Number(n)
165                                } else {
166                                    JsonVal::Null
167                                }
168                            })
169                            .collect::<Vec<_>>(),
170                    )
171                })
172                .collect(),
173        ),
174        Value::KeyedArray(map) => {
175            let mut obj = JsonMap::new();
176            for (k, v) in map {
177                obj.insert(k.clone(), mumu_value_to_json(v));
178            }
179            JsonVal::Object(obj)
180        }
181        Value::MixedArray(items) => {
182            JsonVal::Array(items.iter().map(|it| mumu_value_to_json(it)).collect())
183        }
184        _ => JsonVal::Null,
185    }
186}