Skip to main content

azul_core/
json.rs

1//! JSON value types for C API (data definitions only, no serde_json dependency)
2//!
3//! The actual parsing/serialization lives in `azul_layout::json` which adds
4//! serde_json-based implementations on top of these types.
5
6use alloc::string::String;
7use alloc::vec::Vec;
8use core::fmt;
9use azul_css::{
10    AzString, OptionString, OptionF64, OptionBool,
11    impl_vec, impl_vec_clone, impl_vec_debug, impl_vec_partialeq, impl_vec_mut,
12    impl_result, impl_result_inner,
13    impl_option, impl_option_inner,
14};
15
16// ============================================================================
17// JSON Value Type
18// ============================================================================
19
20/// A generic JSON value that can hold any JSON type
21#[derive(Debug, Clone, PartialEq)]
22#[repr(C)]
23pub struct Json {
24    /// The type of this JSON value
25    pub value_type: JsonType,
26    /// Internal storage - interpretation depends on value_type
27    /// For objects/arrays, this contains serialized data
28    pub internal: JsonInternal,
29}
30
31/// Internal storage for JSON values.
32///
33/// This is a C-FFI-compatible tagged-union-via-struct: all fields always exist,
34/// but only the field(s) corresponding to `JsonType` in the parent `Json` are
35/// meaningful.  For compound types (`Array`, `Object`) the serialized JSON is
36/// stored in `string_value` and re-parsed on each access — this trades repeated
37/// parsing cost for a flat, FFI-safe layout with no interior pointers.
38#[derive(Debug, Clone, PartialEq)]
39#[repr(C)]
40pub struct JsonInternal {
41    /// For strings and serialized objects/arrays
42    pub string_value: AzString,
43    /// For numbers
44    pub number_value: f64,
45    /// For booleans
46    pub bool_value: bool,
47}
48
49impl Default for JsonInternal {
50    fn default() -> Self {
51        Self {
52            string_value: AzString::from(String::new()),
53            number_value: 0.0,
54            bool_value: false,
55        }
56    }
57}
58
59/// Type of a JSON value
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61#[repr(C)]
62pub enum JsonType {
63    /// JSON null
64    Null,
65    /// JSON boolean (true/false)
66    Bool,
67    /// JSON number (stored as f64)
68    Number,
69    /// JSON string
70    String,
71    /// JSON array
72    Array,
73    /// JSON object
74    Object,
75}
76
77/// Error when parsing JSON
78#[derive(Debug, Clone, PartialEq)]
79#[repr(C)]
80pub struct JsonParseError {
81    /// Error message
82    pub message: AzString,
83    /// Line number (if available)
84    pub line: u32,
85    /// Column number (if available)
86    pub column: u32,
87}
88
89impl fmt::Display for JsonParseError {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        if self.line > 0 {
92            write!(f, "{}:{}: {}", self.line, self.column, self.message.as_str())
93        } else {
94            write!(f, "{}", self.message.as_str())
95        }
96    }
97}
98
99#[cfg(feature = "std")]
100impl std::error::Error for JsonParseError {}
101
102/// A key-value pair in a JSON object
103#[derive(Debug, Clone, PartialEq)]
104#[repr(C)]
105pub struct JsonKeyValue {
106    /// The key
107    pub key: AzString,
108    /// The value
109    pub value: Json,
110}
111
112impl JsonKeyValue {
113    /// Create a new key-value pair
114    pub fn create(key: AzString, value: Json) -> Self {
115        Self { key, value }
116    }
117}
118
119// ============================================================================
120// FFI-safe collection types
121// ============================================================================
122
123/// Option type for JsonKeyValue
124impl_option!(JsonKeyValue, OptionJsonKeyValue, copy = false, [Debug, Clone, PartialEq]);
125
126/// Vec of JsonKeyValue (FFI-safe)
127impl_vec!(JsonKeyValue, JsonKeyValueVec, JsonKeyValueVecDestructor, JsonKeyValueVecDestructorType, JsonKeyValueVecSlice, OptionJsonKeyValue);
128impl_vec_clone!(JsonKeyValue, JsonKeyValueVec, JsonKeyValueVecDestructor);
129impl_vec_debug!(JsonKeyValue, JsonKeyValueVec);
130
131impl JsonKeyValueVec {
132    /// Creates a new, heap-allocated JsonKeyValueVec by copying elements from a C array
133    #[inline]
134    pub fn copy_from_array(ptr: *const JsonKeyValue, len: usize) -> Self {
135        if ptr.is_null() || len == 0 {
136            return Self::new();
137        }
138        let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
139        Self::from_vec(slice.iter().cloned().collect())
140    }
141}
142
143// FFI-safe JsonVec using impl_vec! macro
144impl_vec!(Json, JsonVec, JsonVecDestructor, JsonVecDestructorType, JsonVecSlice, OptionJson);
145impl_vec_clone!(Json, JsonVec, JsonVecDestructor);
146impl_vec_debug!(Json, JsonVec);
147impl_vec_partialeq!(Json, JsonVec);
148impl_vec_mut!(Json, JsonVec);
149
150impl JsonVec {
151    /// Creates a new, heap-allocated JsonVec by copying elements from a C array
152    #[inline]
153    pub fn copy_from_array(ptr: *const Json, len: usize) -> Self {
154        if ptr.is_null() || len == 0 {
155            return Self::new();
156        }
157        let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
158        Self::from_vec(slice.iter().cloned().collect())
159    }
160}
161
162// FFI-safe Result type for JSON parsing
163impl_result!(
164    Json,
165    JsonParseError,
166    ResultJsonJsonParseError,
167    copy = false,
168    [Debug, Clone, PartialEq]
169);
170
171// FFI-safe Option types for JSON
172impl_option!(Json, OptionJson, copy = false, [Clone, Debug, PartialEq]);
173impl_option!(JsonVec, OptionJsonVec, copy = false, [Clone, Debug]);
174impl_option!(JsonKeyValueVec, OptionJsonKeyValueVec, copy = false, [Clone, Debug]);
175
176// FFI-safe Option types for JSON value extraction
177// Note: OptionBool and OptionF64 are already exported from azul_css
178impl_option!(i64, OptionI64, [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]);
179
180// ============================================================================
181// Helpers
182// ============================================================================
183
184/// Try to losslessly convert an `f64` to `i64`.
185///
186/// Returns `Some` only when `n` is an integer that fits in `i64` without
187/// overflow.  The upper bound uses `< 2^63` (not `<= i64::MAX as f64`)
188/// because `i64::MAX` cannot be represented exactly in `f64` — the cast
189/// rounds up to `2^63`, which would cause overflow on `n as i64`.
190fn f64_as_i64(n: f64) -> Option<i64> {
191    if n.fract() == 0.0 && n >= -(2_f64.powi(63)) && n < 2_f64.powi(63) {
192        Some(n as i64)
193    } else {
194        None
195    }
196}
197
198// ============================================================================
199// Non-serde methods on Json (pure data, no parsing)
200// ============================================================================
201
202impl Json {
203    /// Create a null JSON value
204    pub fn null() -> Self {
205        Self {
206            value_type: JsonType::Null,
207            internal: JsonInternal::default(),
208        }
209    }
210
211    /// Create a boolean JSON value
212    pub fn bool(value: bool) -> Self {
213        Self {
214            value_type: JsonType::Bool,
215            internal: JsonInternal {
216                string_value: AzString::from(String::new()),
217                number_value: 0.0,
218                bool_value: value,
219            },
220        }
221    }
222
223    /// Create a number JSON value (floating-point)
224    pub fn number(value: f64) -> Self {
225        Self {
226            value_type: JsonType::Number,
227            internal: JsonInternal {
228                string_value: AzString::from(String::new()),
229                number_value: value,
230                bool_value: false,
231            },
232        }
233    }
234
235    /// Create an integer JSON value.
236    ///
237    /// **Note:** the value is stored as `f64` internally, so `i64` values with
238    /// magnitude greater than 2^53 will lose precision silently.
239    pub fn integer(value: i64) -> Self {
240        Self {
241            value_type: JsonType::Number,
242            internal: JsonInternal {
243                string_value: AzString::from(String::new()),
244                number_value: value as f64,
245                bool_value: false,
246            },
247        }
248    }
249
250    /// Create a string JSON value
251    pub fn string(value: impl Into<String>) -> Self {
252        Self {
253            value_type: JsonType::String,
254            internal: JsonInternal {
255                string_value: AzString::from(value.into()),
256                number_value: 0.0,
257                bool_value: false,
258            },
259        }
260    }
261
262    /// Check if this is null
263    pub fn is_null(&self) -> bool {
264        self.value_type == JsonType::Null
265    }
266
267    /// Check if this is a boolean
268    pub fn is_bool(&self) -> bool {
269        self.value_type == JsonType::Bool
270    }
271
272    /// Check if this is a number
273    pub fn is_number(&self) -> bool {
274        self.value_type == JsonType::Number
275    }
276
277    /// Check if this is a string
278    pub fn is_string(&self) -> bool {
279        self.value_type == JsonType::String
280    }
281
282    /// Check if this is an array
283    pub fn is_array(&self) -> bool {
284        self.value_type == JsonType::Array
285    }
286
287    /// Check if this is an object
288    pub fn is_object(&self) -> bool {
289        self.value_type == JsonType::Object
290    }
291
292    /// Get as boolean (returns None if not a bool)
293    pub fn as_bool(&self) -> OptionBool {
294        if self.value_type == JsonType::Bool {
295            OptionBool::Some(self.internal.bool_value)
296        } else {
297            OptionBool::None
298        }
299    }
300
301    /// Get as number (returns None if not a number)
302    pub fn as_number(&self) -> OptionF64 {
303        if self.value_type == JsonType::Number {
304            OptionF64::Some(self.internal.number_value)
305        } else {
306            OptionF64::None
307        }
308    }
309
310    /// Get as integer (returns None if not a number or not an integer)
311    pub fn as_i64(&self) -> OptionI64 {
312        if self.value_type == JsonType::Number {
313            match f64_as_i64(self.internal.number_value) {
314                Some(i) => OptionI64::Some(i),
315                None => OptionI64::None,
316            }
317        } else {
318            OptionI64::None
319        }
320    }
321
322    /// Get as string (returns None if not a string)
323    pub fn as_string(&self) -> OptionString {
324        if self.value_type == JsonType::String {
325            OptionString::Some(self.internal.string_value.clone())
326        } else {
327            OptionString::None
328        }
329    }
330
331    /// Get the raw internal string value (for arrays/objects this is the serialized JSON)
332    pub fn raw_string(&self) -> &str {
333        self.internal.string_value.as_str()
334    }
335}
336
337/// Note: the `Display` output is meant for human-readable / debug display.
338/// String values are quoted but **not** JSON-escaped (no backslash escaping
339/// of embedded quotes, newlines, etc.).  Use `to_json_string()` (requires
340/// the `serde-json` feature) when valid JSON output is needed.
341impl fmt::Display for Json {
342    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343        match self.value_type {
344            JsonType::Null => write!(f, "null"),
345            JsonType::Bool => write!(f, "{}", self.internal.bool_value),
346            JsonType::Number => {
347                let num = self.internal.number_value;
348                if let Some(i) = f64_as_i64(num) {
349                    write!(f, "{}", i)
350                } else {
351                    write!(f, "{}", num)
352                }
353            }
354            JsonType::String => write!(f, "\"{}\"", self.internal.string_value.as_str()),
355            JsonType::Array | JsonType::Object => {
356                write!(f, "{}", self.internal.string_value.as_str())
357            }
358        }
359    }
360}
361
362// ============================================================================
363// serde_json-dependent methods (gated behind "serde-json" feature)
364// ============================================================================
365
366#[cfg(feature = "serde-json")]
367impl Json {
368    /// Parse JSON from a string
369    pub fn parse(s: &str) -> Result<Self, JsonParseError> {
370        let value: serde_json::Value = serde_json::from_str(s).map_err(|e| {
371            JsonParseError {
372                message: AzString::from(alloc::format!("{}", e)),
373                line: e.line() as u32,
374                column: e.column() as u32,
375            }
376        })?;
377        Ok(Self::from_serde_value(value))
378    }
379
380    /// Parse JSON from bytes (UTF-8)
381    pub fn parse_bytes(bytes: &[u8]) -> Result<Self, JsonParseError> {
382        let value: serde_json::Value = serde_json::from_slice(bytes).map_err(|e| {
383            JsonParseError {
384                message: AzString::from(alloc::format!("{}", e)),
385                line: e.line() as u32,
386                column: e.column() as u32,
387            }
388        })?;
389        Ok(Self::from_serde_value(value))
390    }
391
392    /// Convert from serde_json::Value
393    pub fn from_serde_value(value: serde_json::Value) -> Self {
394        match value {
395            serde_json::Value::Null => Self::null(),
396            serde_json::Value::Bool(b) => Self::bool(b),
397            serde_json::Value::Number(n) => Self::number(n.as_f64().unwrap_or(0.0)),
398            serde_json::Value::String(s) => Self::string(s),
399            serde_json::Value::Array(arr) => {
400                let json_str = serde_json::to_string(&serde_json::Value::Array(arr)).unwrap_or_default();
401                Self {
402                    value_type: JsonType::Array,
403                    internal: JsonInternal {
404                        string_value: AzString::from(json_str),
405                        number_value: 0.0,
406                        bool_value: false,
407                    },
408                }
409            }
410            serde_json::Value::Object(obj) => {
411                let json_str = serde_json::to_string(&serde_json::Value::Object(obj)).unwrap_or_default();
412                Self {
413                    value_type: JsonType::Object,
414                    internal: JsonInternal {
415                        string_value: AzString::from(json_str),
416                        number_value: 0.0,
417                        bool_value: false,
418                    },
419                }
420            }
421        }
422    }
423
424    /// Convert this Json to a serde_json::Value
425    pub fn to_serde_value(&self) -> serde_json::Value {
426        match self.value_type {
427            JsonType::Null => serde_json::Value::Null,
428            JsonType::Bool => serde_json::Value::Bool(self.internal.bool_value),
429            JsonType::Number => {
430                let num = self.internal.number_value;
431                if let Some(i) = f64_as_i64(num) {
432                    serde_json::Value::Number(serde_json::Number::from(i))
433                } else {
434                    serde_json::Number::from_f64(num)
435                        .map(serde_json::Value::Number)
436                        .unwrap_or(serde_json::Value::Null)
437                }
438            }
439            JsonType::String => serde_json::Value::String(self.internal.string_value.as_str().to_string()),
440            JsonType::Array | JsonType::Object => {
441                serde_json::from_str(self.internal.string_value.as_str())
442                    .unwrap_or(serde_json::Value::Null)
443            }
444        }
445    }
446
447    /// Create a JSON array from a vector of JSON values
448    pub fn array(values: JsonVec) -> Self {
449        let serde_array: Vec<serde_json::Value> = values
450            .as_slice()
451            .iter()
452            .map(|j| j.to_serde_value())
453            .collect();
454        let json_str = serde_json::to_string(&serde_json::Value::Array(serde_array))
455            .unwrap_or_else(|_| "[]".to_string());
456        Self {
457            value_type: JsonType::Array,
458            internal: JsonInternal {
459                string_value: AzString::from(json_str),
460                number_value: 0.0,
461                bool_value: false,
462            },
463        }
464    }
465
466    /// Create a JSON object from key-value pairs
467    pub fn object(entries: JsonKeyValueVec) -> Self {
468        let mut map = serde_json::Map::new();
469        for kv in entries.as_slice() {
470            map.insert(kv.key.as_str().to_string(), kv.value.to_serde_value());
471        }
472        let json_str = serde_json::to_string(&serde_json::Value::Object(map))
473            .unwrap_or_else(|_| "{}".to_string());
474        Self {
475            value_type: JsonType::Object,
476            internal: JsonInternal {
477                string_value: AzString::from(json_str),
478                number_value: 0.0,
479                bool_value: false,
480            },
481        }
482    }
483
484    /// Get the number of elements (for arrays) or keys (for objects)
485    pub fn len(&self) -> usize {
486        match self.value_type {
487            JsonType::Array => {
488                if let Ok(serde_json::Value::Array(arr)) = serde_json::from_str(self.internal.string_value.as_str()) {
489                    arr.len()
490                } else {
491                    0
492                }
493            }
494            JsonType::Object => {
495                if let Ok(serde_json::Value::Object(obj)) = serde_json::from_str(self.internal.string_value.as_str()) {
496                    obj.len()
497                } else {
498                    0
499                }
500            }
501            _ => 0,
502        }
503    }
504
505    /// Check if empty (for arrays/objects)
506    pub fn is_empty(&self) -> bool {
507        self.len() == 0
508    }
509
510    /// Get array element by index
511    pub fn get_index(&self, index: usize) -> Option<Json> {
512        if self.value_type != JsonType::Array { return None; }
513        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
514        if let serde_json::Value::Array(arr) = value {
515            arr.get(index).map(|v| Self::from_serde_value(v.clone()))
516        } else {
517            None
518        }
519    }
520
521    /// Get object value by key
522    pub fn get_key(&self, key: &str) -> Option<Json> {
523        if self.value_type != JsonType::Object { return None; }
524        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
525        if let serde_json::Value::Object(obj) = value {
526            obj.get(key).map(|v| Self::from_serde_value(v.clone()))
527        } else {
528            None
529        }
530    }
531
532    /// Get all keys of an object
533    pub fn keys(&self) -> Vec<AzString> {
534        if self.value_type != JsonType::Object { return Vec::new(); }
535        let value: serde_json::Value = match serde_json::from_str(self.internal.string_value.as_str()) {
536            Ok(v) => v,
537            Err(_) => return Vec::new(),
538        };
539        if let serde_json::Value::Object(obj) = value {
540            obj.keys().map(|k| AzString::from(k.clone())).collect()
541        } else {
542            Vec::new()
543        }
544    }
545
546    /// Convert array to Vec<Json>
547    pub fn to_array(&self) -> Option<JsonVec> {
548        if self.value_type != JsonType::Array { return None; }
549        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
550        if let serde_json::Value::Array(arr) = value {
551            Some(arr.into_iter().map(Self::from_serde_value).collect())
552        } else {
553            None
554        }
555    }
556
557    /// Convert object to Vec<JsonKeyValue>
558    pub fn to_object(&self) -> Option<JsonKeyValueVec> {
559        if self.value_type != JsonType::Object { return None; }
560        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
561        if let serde_json::Value::Object(obj) = value {
562            Some(obj.into_iter().map(|(k, v)| JsonKeyValue {
563                key: AzString::from(k),
564                value: Self::from_serde_value(v),
565            }).collect())
566        } else {
567            None
568        }
569    }
570
571    /// Serialize to JSON string (returns AzString)
572    pub fn to_json_string(&self) -> AzString {
573        match self.value_type {
574            JsonType::Null => AzString::from(alloc::string::String::from("null")),
575            JsonType::Bool => AzString::from(if self.internal.bool_value { alloc::string::String::from("true") } else { alloc::string::String::from("false") }),
576            JsonType::Number => {
577                let num = self.internal.number_value;
578                if let Some(i) = f64_as_i64(num) {
579                    AzString::from(alloc::format!("{}", i))
580                } else {
581                    AzString::from(alloc::format!("{}", num))
582                }
583            }
584            JsonType::String => {
585                let escaped = serde_json::to_string(self.internal.string_value.as_str()).unwrap_or_default();
586                AzString::from(escaped)
587            }
588            JsonType::Array | JsonType::Object => {
589                self.internal.string_value.clone()
590            }
591        }
592    }
593
594    /// Serialize to pretty-printed JSON string
595    pub fn to_string_pretty(&self) -> AzString {
596        match self.value_type {
597            JsonType::Null | JsonType::Bool | JsonType::Number | JsonType::String => {
598                self.to_json_string()
599            }
600            JsonType::Array | JsonType::Object => {
601                if let Ok(value) = serde_json::from_str::<serde_json::Value>(self.internal.string_value.as_str()) {
602                    AzString::from(serde_json::to_string_pretty(&value).unwrap_or_default())
603                } else {
604                    self.internal.string_value.clone()
605                }
606            }
607        }
608    }
609
610    /// Access a nested value using a JSON Pointer (RFC 6901).
611    pub fn jq(&self, path: &str) -> Json {
612        match self.value_type {
613            JsonType::Null | JsonType::Bool | JsonType::Number | JsonType::String => {
614                if path.is_empty() { self.clone() } else { Json::null() }
615            }
616            JsonType::Array | JsonType::Object => {
617                let value: serde_json::Value = match serde_json::from_str(self.internal.string_value.as_str()) {
618                    Ok(v) => v,
619                    Err(_) => return Json::null(),
620                };
621                match value.pointer(path) {
622                    Some(v) => Self::from_serde_value(v.clone()),
623                    None => Json::null(),
624                }
625            }
626        }
627    }
628
629    /// Access nested values using a JSON Pointer with wildcard support.
630    pub fn jq_all(&self, path: &str) -> JsonVec {
631        let result = match self.value_type {
632            JsonType::Null | JsonType::Bool | JsonType::Number | JsonType::String => {
633                if path.is_empty() { vec![self.clone()] } else { vec![] }
634            }
635            JsonType::Array | JsonType::Object => {
636                let value: serde_json::Value = match serde_json::from_str(self.internal.string_value.as_str()) {
637                    Ok(v) => v,
638                    Err(_) => return JsonVec::from_vec(vec![]),
639                };
640                Self::jq_all_recursive(&value, path)
641            }
642        };
643        JsonVec::from_vec(result)
644    }
645
646    /// Recursive helper for jq_all that handles wildcards
647    fn jq_all_recursive(value: &serde_json::Value, path: &str) -> Vec<Json> {
648        if path.is_empty() {
649            return vec![Self::from_serde_value(value.clone())];
650        }
651        if !path.starts_with('/') { return vec![]; }
652        let rest = &path[1..];
653        let (component, remaining) = match rest.find('/') {
654            Some(idx) => (&rest[..idx], &rest[idx..]),
655            None => (rest, ""),
656        };
657        if component == "*" {
658            let mut results = Vec::new();
659            match value {
660                serde_json::Value::Array(arr) => {
661                    for item in arr { results.extend(Self::jq_all_recursive(item, remaining)); }
662                }
663                serde_json::Value::Object(obj) => {
664                    for (_key, val) in obj { results.extend(Self::jq_all_recursive(val, remaining)); }
665                }
666                _ => {}
667            }
668            results
669        } else {
670            match value {
671                serde_json::Value::Array(arr) => {
672                    if let Ok(idx) = component.parse::<usize>() {
673                        if let Some(item) = arr.get(idx) {
674                            return Self::jq_all_recursive(item, remaining);
675                        }
676                    }
677                    vec![]
678                }
679                serde_json::Value::Object(obj) => {
680                    if let Some(val) = obj.get(component) {
681                        return Self::jq_all_recursive(val, remaining);
682                    }
683                    vec![]
684                }
685                _ => vec![],
686            }
687        }
688    }
689}