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}