air_interpreter_value/value/
mod.rs

1/*
2 * Copyright 2024 Fluence Labs Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * This file is based on serde_json crate by Erick Tryzelaar and David Tolnay
19 * licensed under conditions of MIT License and Apache License, Version 2.0.
20 */
21
22//! The JValue enum, a loosely typed way of representing any valid JSON value.
23//!
24
25mod de;
26mod from;
27mod index;
28mod partial_eq;
29mod ser;
30
31use core::fmt::{self, Debug, Display};
32use core::mem;
33use core::str;
34use std::io;
35use std::rc::Rc;
36
37pub use self::index::Index;
38use crate::JsonString;
39pub use crate::Map;
40pub use serde::ser::Serializer;
41pub use serde_json::Number;
42
43/// Represents any valid JSON value with a cheap to clone Rc-based representation.
44#[derive(Clone, Eq, PartialEq)]
45pub enum JValue {
46    /// Represents a JSON null value.
47    Null,
48
49    /// Represents a JSON boolean.
50    Bool(bool),
51
52    /// Represents a JSON number, whether integer or floating point.
53    Number(Number),
54
55    /// Represents a JSON string.
56    String(JsonString),
57
58    /// Represents a JSON array.
59    Array(Rc<[JValue]>),
60
61    /// Represents a JSON object.
62    ///
63    /// By default the map is backed by a BTreeMap. Enable the `preserve_order`
64    /// feature of serde_json to use IndexMap instead, which preserves
65    /// entries in the order they are inserted into the map. In particular, this
66    /// allows JSON data to be deserialized into a JValue and serialized to a
67    /// string while retaining the order of map keys in the input.
68    Object(Rc<Map<JsonString, JValue>>),
69}
70
71impl Debug for JValue {
72    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
73        match self {
74            JValue::Null => formatter.write_str("Null"),
75            JValue::Bool(boolean) => write!(formatter, "Bool({})", boolean),
76            JValue::Number(number) => Debug::fmt(number, formatter),
77            JValue::String(string) => write!(formatter, "String({:?})", string),
78            JValue::Array(vec) => {
79                tri!(formatter.write_str("Array "));
80                Debug::fmt(vec, formatter)
81            }
82            JValue::Object(map) => {
83                tri!(formatter.write_str("Object "));
84                Debug::fmt(&**map, formatter)
85            }
86        }
87    }
88}
89
90impl Display for JValue {
91    /// Display a JSON value as a string.
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        struct WriterFormatter<'a, 'b: 'a> {
94            inner: &'a mut fmt::Formatter<'b>,
95        }
96
97        impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> {
98            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
99                // Safety: the serializer below only emits valid utf8 when using
100                // the default formatter.
101                let s = unsafe { str::from_utf8_unchecked(buf) };
102                tri!(self.inner.write_str(s).map_err(io_error));
103                Ok(buf.len())
104            }
105
106            fn flush(&mut self) -> io::Result<()> {
107                Ok(())
108            }
109        }
110
111        fn io_error(_: fmt::Error) -> io::Error {
112            // Error value does not matter because Display impl just maps it
113            // back to fmt::Error.
114            io::Error::new(io::ErrorKind::Other, "fmt error")
115        }
116
117        let alternate = f.alternate();
118        let mut wr = WriterFormatter { inner: f };
119        if alternate {
120            // {:#}
121            serde_json::ser::to_writer_pretty(&mut wr, self).map_err(|_| fmt::Error)
122        } else {
123            // {}
124            serde_json::ser::to_writer(&mut wr, self).map_err(|_| fmt::Error)
125        }
126    }
127}
128
129fn parse_index(s: &str) -> Option<usize> {
130    if s.starts_with('+') || (s.starts_with('0') && s.len() != 1) {
131        return None;
132    }
133    s.parse().ok()
134}
135
136impl JValue {
137    #[inline]
138    pub fn string(s: impl Into<Rc<str>>) -> Self {
139        Self::String(s.into())
140    }
141
142    #[inline]
143    pub fn array(vec: impl Into<Rc<[JValue]>>) -> Self {
144        Self::Array(vec.into())
145    }
146
147    pub fn array_from_iter(into_iter: impl IntoIterator<Item = impl Into<JValue>>) -> Self {
148        Self::Array(into_iter.into_iter().map(Into::into).collect())
149    }
150
151    pub fn object(map: impl Into<Map<JsonString, JValue>>) -> Self {
152        Self::Object(Rc::new(map.into()))
153    }
154
155    pub fn object_from_pairs(
156        into_iter: impl IntoIterator<Item = (impl Into<JsonString>, impl Into<JValue>)>,
157    ) -> Self {
158        Self::Object(Rc::new(
159            into_iter
160                .into_iter()
161                .map(|(k, v)| (k.into(), v.into()))
162                .collect(),
163        ))
164    }
165
166    /// Index into a JSON array or map. A string index can be used to access a
167    /// value in a map, and a usize index can be used to access an element of an
168    /// array.
169    ///
170    /// Returns `None` if the type of `self` does not match the type of the
171    /// index, for example if the index is a string and `self` is an array or a
172    /// number. Also returns `None` if the given key does not exist in the map
173    /// or the given index is not within the bounds of the array.
174    ///
175    /// Square brackets can also be used to index into a value in a more concise
176    /// way. This returns `JValue::Null` in cases where `get` would have returned
177    /// `None`.
178    pub fn get<I: Index>(&self, index: I) -> Option<&JValue> {
179        index.index_into(self)
180    }
181
182    /// Returns true if the `JValue` is an Object. Returns false otherwise.
183    ///
184    /// For any JValue on which `is_object` returns true, `as_object` and
185    /// `as_object_mut` are guaranteed to return the map representation of the
186    /// object.
187    #[inline]
188    pub fn is_object(&self) -> bool {
189        self.as_object().is_some()
190    }
191
192    /// If the `JValue` is an Object, returns the associated Map. Returns None
193    /// otherwise.
194    #[inline]
195    pub fn as_object(&self) -> Option<&Map<JsonString, JValue>> {
196        match self {
197            JValue::Object(map) => Some(map),
198            _ => None,
199        }
200    }
201
202    /// Returns true if the `JValue` is an Array. Returns false otherwise.
203    ///
204    /// For any JValue on which `is_array` returns true, `as_array` and
205    /// `as_array_mut` are guaranteed to return the vector representing the
206    /// array.
207    #[inline]
208    pub fn is_array(&self) -> bool {
209        self.as_array().is_some()
210    }
211
212    /// If the `JValue` is an Array, returns the associated vector. Returns None
213    /// otherwise.
214    #[inline]
215    pub fn as_array(&self) -> Option<&[JValue]> {
216        match self {
217            JValue::Array(array) => Some(array),
218            _ => None,
219        }
220    }
221
222    /// Returns true if the `JValue` is a String. Returns false otherwise.
223    ///
224    /// For any JValue on which `is_string` returns true, `as_str` is guaranteed
225    /// to return the string slice.
226    #[inline]
227    pub fn is_string(&self) -> bool {
228        self.as_str().is_some()
229    }
230
231    /// If the `JValue` is a string, returns the associated str. Returns None
232    /// otherwise.
233    #[inline]
234    pub fn as_str(&self) -> Option<&JsonString> {
235        match self {
236            JValue::String(s) => Some(s),
237            _ => None,
238        }
239    }
240
241    /// Returns true if the `JValue` is a Number. Returns false otherwise.
242    #[inline]
243    pub fn is_number(&self) -> bool {
244        matches!(self, JValue::Number(_))
245    }
246
247    /// If the `JValue` is a Number, returns the associated [`Number`]. Returns
248    /// None otherwise.
249    #[inline]
250    pub fn as_number(&self) -> Option<&Number> {
251        match self {
252            JValue::Number(number) => Some(number),
253            _ => None,
254        }
255    }
256
257    /// Returns true if the `JValue` is an integer between `i64::MIN` and
258    /// `i64::MAX`.
259    ///
260    /// For any JValue on which `is_i64` returns true, `as_i64` is guaranteed to
261    /// return the integer value.
262    #[inline]
263    pub fn is_i64(&self) -> bool {
264        match self {
265            JValue::Number(n) => n.is_i64(),
266            _ => false,
267        }
268    }
269
270    /// Returns true if the `JValue` is an integer between zero and `u64::MAX`.
271    ///
272    /// For any JValue on which `is_u64` returns true, `as_u64` is guaranteed to
273    /// return the integer value.
274    #[inline]
275    pub fn is_u64(&self) -> bool {
276        match self {
277            JValue::Number(n) => n.is_u64(),
278            _ => false,
279        }
280    }
281
282    /// Returns true if the `JValue` is a number that can be represented by f64.
283    ///
284    /// For any JValue on which `is_f64` returns true, `as_f64` is guaranteed to
285    /// return the floating point value.
286    ///
287    /// Currently this function returns true if and only if both `is_i64` and
288    /// `is_u64` return false but this is not a guarantee in the future.
289    #[inline]
290    pub fn is_f64(&self) -> bool {
291        match self {
292            JValue::Number(n) => n.is_f64(),
293            _ => false,
294        }
295    }
296
297    /// If the `JValue` is an integer, represent it as i64 if possible. Returns
298    /// None otherwise.
299    #[inline]
300    pub fn as_i64(&self) -> Option<i64> {
301        match self {
302            JValue::Number(n) => n.as_i64(),
303            _ => None,
304        }
305    }
306
307    /// If the `JValue` is an integer, represent it as u64 if possible. Returns
308    /// None otherwise.
309    #[inline]
310    pub fn as_u64(&self) -> Option<u64> {
311        match self {
312            JValue::Number(n) => n.as_u64(),
313            _ => None,
314        }
315    }
316
317    /// If the `JValue` is a number, represent it as f64 if possible. Returns
318    /// None otherwise.
319    #[inline]
320    pub fn as_f64(&self) -> Option<f64> {
321        match self {
322            JValue::Number(n) => n.as_f64(),
323            _ => None,
324        }
325    }
326
327    /// Returns true if the `JValue` is a Boolean. Returns false otherwise.
328    ///
329    /// For any JValue on which `is_boolean` returns true, `as_bool` is
330    /// guaranteed to return the boolean value.
331    #[inline]
332    pub fn is_boolean(&self) -> bool {
333        self.as_bool().is_some()
334    }
335
336    /// If the `JValue` is a Boolean, returns the associated bool. Returns None
337    /// otherwise.
338    #[inline]
339    pub fn as_bool(&self) -> Option<bool> {
340        match *self {
341            JValue::Bool(b) => Some(b),
342            _ => None,
343        }
344    }
345
346    /// Returns true if the `JValue` is a Null. Returns false otherwise.
347    ///
348    /// For any JValue on which `is_null` returns true, `as_null` is guaranteed
349    /// to return `Some(())`.
350    #[inline]
351    pub fn is_null(&self) -> bool {
352        self.as_null().is_some()
353    }
354
355    /// If the `JValue` is a Null, returns (). Returns None otherwise.
356    #[inline]
357    pub fn as_null(&self) -> Option<()> {
358        match *self {
359            JValue::Null => Some(()),
360            _ => None,
361        }
362    }
363
364    /// Looks up a value by a JSON Pointer.
365    ///
366    /// JSON Pointer defines a string syntax for identifying a specific value
367    /// within a JavaScript Object Notation (JSON) document.
368    ///
369    /// A Pointer is a Unicode string with the reference tokens separated by `/`.
370    /// Inside tokens `/` is replaced by `~1` and `~` is replaced by `~0`. The
371    /// addressed value is returned and if there is no such value `None` is
372    /// returned.
373    ///
374    /// For more information read [RFC6901](https://tools.ietf.org/html/rfc6901).
375    pub fn pointer(&self, pointer: &str) -> Option<&JValue> {
376        if pointer.is_empty() {
377            return Some(self);
378        }
379        if !pointer.starts_with('/') {
380            return None;
381        }
382        pointer
383            .split('/')
384            .skip(1)
385            .map(|x| x.replace("~1", "/").replace("~0", "~"))
386            .try_fold(self, |target, token| match target {
387                JValue::Object(map) => map.get(token.as_str()),
388                JValue::Array(list) => parse_index(&token).and_then(|x| list.get(x)),
389                _ => None,
390            })
391    }
392
393    /// Takes the value out of the `JValue`, leaving a `Null` in its place.
394    #[inline]
395    pub fn take(&mut self) -> JValue {
396        mem::replace(self, JValue::Null)
397    }
398}
399
400/// The default value is `JValue::Null`.
401impl Default for JValue {
402    #[inline]
403    fn default() -> JValue {
404        JValue::Null
405    }
406}