Skip to main content

pepl_stdlib/modules/
json.rs

1//! `json` stdlib module — JSON parsing and serialization.
2//!
3//! Functions: parse, stringify.
4//! Max parse depth: 32 (prevents stack overflow on deeply nested JSON).
5
6use std::collections::BTreeMap;
7
8use crate::error::StdlibError;
9use crate::module::StdlibModule;
10use crate::value::{ResultValue, Value};
11
12/// Maximum allowed nesting depth when parsing JSON.
13const MAX_DEPTH: usize = 32;
14
15/// The `json` stdlib module.
16pub struct JsonModule;
17
18impl JsonModule {
19    pub fn new() -> Self {
20        Self
21    }
22}
23
24impl Default for JsonModule {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl StdlibModule for JsonModule {
31    fn name(&self) -> &'static str {
32        "json"
33    }
34
35    fn has_function(&self, function: &str) -> bool {
36        matches!(function, "parse" | "stringify")
37    }
38
39    fn call(&self, function: &str, args: Vec<Value>) -> Result<Value, StdlibError> {
40        match function {
41            "parse" => self.parse(args),
42            "stringify" => self.stringify(args),
43            _ => Err(StdlibError::unknown_function("json", function)),
44        }
45    }
46}
47
48impl JsonModule {
49    /// json.parse(s) → Result<any, string>
50    /// Parses a JSON string into a PEPL Value.
51    fn parse(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
52        if args.len() != 1 {
53            return Err(StdlibError::wrong_args("json.parse", 1, args.len()));
54        }
55        let s = extract_string("json.parse", &args[0], 1)?;
56
57        match serde_json::from_str::<serde_json::Value>(s) {
58            Ok(json_val) => match json_to_value(&json_val, 0) {
59                Ok(v) => Ok(v.ok()),
60                Err(msg) => Ok(Value::String(msg).err()),
61            },
62            Err(e) => Ok(Value::String(format!("JSON parse error: {}", e)).err()),
63        }
64    }
65
66    /// json.stringify(value) → string
67    /// Converts a PEPL Value to a JSON string.
68    fn stringify(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
69        if args.len() != 1 {
70            return Err(StdlibError::wrong_args("json.stringify", 1, args.len()));
71        }
72        let json_val = value_to_json(&args[0]);
73        Ok(Value::String(
74            serde_json::to_string(&json_val).unwrap_or_else(|_| "null".to_string()),
75        ))
76    }
77}
78
79// ── JSON ↔ Value conversion ────────────────────────────────────────────────
80
81/// Convert a serde_json::Value to a PEPL Value, respecting depth limits.
82fn json_to_value(json: &serde_json::Value, depth: usize) -> Result<Value, String> {
83    if depth > MAX_DEPTH {
84        return Err(format!(
85            "JSON nesting exceeds maximum depth of {}",
86            MAX_DEPTH
87        ));
88    }
89
90    match json {
91        serde_json::Value::Null => Ok(Value::Nil),
92        serde_json::Value::Bool(b) => Ok(Value::Bool(*b)),
93        serde_json::Value::Number(n) => Ok(Value::Number(n.as_f64().unwrap_or(0.0))),
94        serde_json::Value::String(s) => Ok(Value::String(s.clone())),
95        serde_json::Value::Array(arr) => {
96            let mut items = Vec::with_capacity(arr.len());
97            for item in arr {
98                items.push(json_to_value(item, depth + 1)?);
99            }
100            Ok(Value::List(items))
101        }
102        serde_json::Value::Object(obj) => {
103            let mut fields = BTreeMap::new();
104            for (key, val) in obj {
105                fields.insert(key.clone(), json_to_value(val, depth + 1)?);
106            }
107            Ok(Value::record(fields))
108        }
109    }
110}
111
112/// Convert a PEPL Value to a serde_json::Value for serialization.
113fn value_to_json(value: &Value) -> serde_json::Value {
114    match value {
115        Value::Nil => serde_json::Value::Null,
116        Value::Bool(b) => serde_json::Value::Bool(*b),
117        Value::Number(n) => {
118            if n.is_finite() {
119                serde_json::Value::Number(
120                    serde_json::Number::from_f64(*n).unwrap_or_else(|| serde_json::Number::from(0)),
121                )
122            } else {
123                serde_json::Value::Null // NaN/Infinity → null
124            }
125        }
126        Value::String(s) => serde_json::Value::String(s.clone()),
127        Value::List(items) => serde_json::Value::Array(items.iter().map(value_to_json).collect()),
128        Value::Record { fields, .. } => {
129            let obj: serde_json::Map<String, serde_json::Value> = fields
130                .iter()
131                .map(|(k, v)| (k.clone(), value_to_json(v)))
132                .collect();
133            serde_json::Value::Object(obj)
134        }
135        Value::Color { r, g, b, a } => {
136            let mut obj = serde_json::Map::new();
137            obj.insert("r".into(), value_to_json(&Value::Number(*r)));
138            obj.insert("g".into(), value_to_json(&Value::Number(*g)));
139            obj.insert("b".into(), value_to_json(&Value::Number(*b)));
140            obj.insert("a".into(), value_to_json(&Value::Number(*a)));
141            serde_json::Value::Object(obj)
142        }
143        Value::Result(rv) => match rv.as_ref() {
144            ResultValue::Ok(v) => {
145                let mut obj = serde_json::Map::new();
146                obj.insert("ok".into(), value_to_json(v));
147                serde_json::Value::Object(obj)
148            }
149            ResultValue::Err(v) => {
150                let mut obj = serde_json::Map::new();
151                obj.insert("err".into(), value_to_json(v));
152                serde_json::Value::Object(obj)
153            }
154        },
155        Value::SumVariant {
156            type_name,
157            variant,
158            fields,
159        } => {
160            let mut obj = serde_json::Map::new();
161            obj.insert("_type".into(), serde_json::Value::String(type_name.clone()));
162            obj.insert(
163                "_variant".into(),
164                serde_json::Value::String(variant.clone()),
165            );
166            if !fields.is_empty() {
167                obj.insert(
168                    "_fields".into(),
169                    serde_json::Value::Array(fields.iter().map(value_to_json).collect()),
170                );
171            }
172            serde_json::Value::Object(obj)
173        }
174        Value::Function(_) => serde_json::Value::String("<function>".to_string()),
175    }
176}
177
178// ── Helpers ──────────────────────────────────────────────────────────────────
179
180fn extract_string<'a>(func: &str, val: &'a Value, pos: usize) -> Result<&'a str, StdlibError> {
181    match val {
182        Value::String(s) => Ok(s),
183        _ => Err(StdlibError::type_mismatch(
184            func,
185            pos,
186            "string",
187            val.type_name(),
188        )),
189    }
190}