Skip to content

Commit 0a540a4

Browse files
committed
feat(rad): decode JSON more efficiently
1 parent d76e7d5 commit 0a540a4

File tree

2 files changed

+218
-93
lines changed

2 files changed

+218
-93
lines changed

rad/src/operators/string.rs

Lines changed: 35 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,13 @@ const MAX_DEPTH: u8 = 20;
1919
const DEFAULT_THOUSANDS_SEPARATOR: &str = ",";
2020
const DEFAULT_DECIMAL_SEPARATOR: &str = ".";
2121

22+
/// Parse `RadonTypes` from a JSON-encoded `RadonString`.
2223
pub fn parse_json(input: &RadonString) -> Result<RadonTypes, RadError> {
23-
match json::parse(&input.value()) {
24-
Ok(json_value) => {
25-
let value = json_to_cbor(&json_value);
26-
RadonTypes::try_from(value)
27-
}
28-
Err(json_error) => Err(RadError::JsonParse {
29-
description: json_error.to_string(),
30-
}),
31-
}
24+
let json_value = json::parse(&input.value()).map_err(|err| RadError::JsonParse {
25+
description: err.to_string(),
26+
})?;
27+
28+
RadonTypes::try_from(json_value)
3229
}
3330

3431
pub fn parse_json_map(input: &RadonString) -> Result<RadonMap, RadError> {
@@ -282,39 +279,6 @@ pub fn string_match(input: &RadonString, args: &[Value]) -> Result<RadonTypes, R
282279
.unwrap_or(Ok(temp_def))
283280
}
284281

285-
/// Converts a JSON value (`json::JsonValue`) into a CBOR value (`serde_cbor::value::Value`).
286-
/// Some conversions are totally straightforward, but some others need some more logic (e.g.
287-
/// telling apart integers from floats).
288-
#[allow(clippy::cast_possible_truncation)]
289-
fn json_to_cbor(value: &json::JsonValue) -> Value {
290-
match value {
291-
json::JsonValue::Array(value) => Value::Array(value.iter().map(json_to_cbor).collect()),
292-
json::JsonValue::Object(value) => {
293-
let entries = value
294-
.iter()
295-
.map(|(key, value)| (Value::Text(String::from(key)), json_to_cbor(value)))
296-
.collect();
297-
Value::Map(entries)
298-
}
299-
json::JsonValue::Short(value) => Value::Text(String::from(value.as_str())),
300-
json::JsonValue::Number(value) => {
301-
let (_, _, exponent) = value.as_parts();
302-
let floating = f64::from(*value);
303-
// Cast the float into an integer if it has no fractional part and its value will fit
304-
// into the range of `i128` (38 is the biggest power of 10 that `i128` can safely hold)
305-
if floating.fract() == 0.0 && exponent.unsigned_abs() < 38 {
306-
// This cast is assumed to be safe as per the previous guard
307-
Value::Integer(floating as i128)
308-
} else {
309-
Value::Float(floating)
310-
}
311-
}
312-
json::JsonValue::String(value) => Value::Text(String::from(value.as_str())),
313-
json::JsonValue::Boolean(b) => Value::Bool(*b),
314-
json::JsonValue::Null => Value::Null,
315-
}
316-
}
317-
318282
/// Replace thousands and decimals separators in a `String`.
319283
#[inline]
320284
pub fn replace_separators(
@@ -1168,48 +1132,48 @@ mod tests {
11681132
}
11691133

11701134
#[test]
1171-
fn test_json_numbers_to_cbor_numbers() {
1135+
fn test_json_numbers_to_radon_numbers() {
11721136
use json::{number::Number, JsonValue};
11731137

11741138
let json = JsonValue::Number(Number::from(2.0));
1175-
let resulting_cbor = json_to_cbor(&json);
1176-
let expected_cbor = serde_cbor::Value::Integer(2);
1177-
assert_eq!(resulting_cbor, expected_cbor);
1139+
let resulting_radon = RadonTypes::try_from(json).unwrap();
1140+
let expected_radon = RadonInteger::from(2).into();
1141+
assert_eq!(resulting_radon, expected_radon);
11781142

11791143
let json = JsonValue::Number(Number::from(20.0));
1180-
let resulting_cbor = json_to_cbor(&json);
1181-
let expected_cbor = serde_cbor::Value::Integer(20);
1182-
assert_eq!(resulting_cbor, expected_cbor);
1144+
let resulting_radon = RadonTypes::try_from(json).unwrap();
1145+
let expected_radon = RadonInteger::from(20).into();
1146+
assert_eq!(resulting_radon, expected_radon);
11831147

11841148
let json = JsonValue::Number(Number::from(2_000.0));
1185-
let resulting_cbor = json_to_cbor(&json);
1186-
let expected_cbor = serde_cbor::Value::Integer(2_000);
1187-
assert_eq!(resulting_cbor, expected_cbor);
1149+
let resulting_radon = RadonTypes::try_from(json).unwrap();
1150+
let expected_radon = RadonInteger::from(2_000).into();
1151+
assert_eq!(resulting_radon, expected_radon);
11881152

11891153
let json = JsonValue::Number(Number::from(2_000_000.0));
1190-
let resulting_cbor = json_to_cbor(&json);
1191-
let expected_cbor = serde_cbor::Value::Integer(2_000_000);
1192-
assert_eq!(resulting_cbor, expected_cbor);
1154+
let resulting_radon = RadonTypes::try_from(json).unwrap();
1155+
let expected_radon = RadonInteger::from(2_000_000).into();
1156+
assert_eq!(resulting_radon, expected_radon);
11931157

11941158
let json = JsonValue::Number(Number::from(std::f64::consts::PI));
1195-
let resulting_cbor = json_to_cbor(&json);
1196-
let expected_cbor = serde_cbor::Value::Float(std::f64::consts::PI);
1197-
assert_eq!(resulting_cbor, expected_cbor);
1159+
let resulting_radon = RadonTypes::try_from(json).unwrap();
1160+
let expected_radon = RadonFloat::from(std::f64::consts::PI).into();
1161+
assert_eq!(resulting_radon, expected_radon);
11981162

11991163
let json = JsonValue::Number(Number::from(1e100));
1200-
let resulting_cbor = json_to_cbor(&json);
1201-
let expected_cbor = serde_cbor::Value::Float(1e100);
1202-
assert_eq!(resulting_cbor, expected_cbor);
1164+
let resulting_radon = RadonTypes::try_from(json).unwrap();
1165+
let expected_radon = RadonFloat::from(1e100).into();
1166+
assert_eq!(resulting_radon, expected_radon);
12031167

12041168
let json = JsonValue::Number(Number::from(4.0));
1205-
let resulting_cbor = json_to_cbor(&json);
1206-
let expected_cbor = serde_cbor::Value::Integer(4);
1207-
assert_eq!(resulting_cbor, expected_cbor);
1169+
let resulting_radon = RadonTypes::try_from(json).unwrap();
1170+
let expected_radon = RadonInteger::from(4).into();
1171+
assert_eq!(resulting_radon, expected_radon);
12081172

12091173
let json = JsonValue::Number(Number::from(4.1));
1210-
let resulting_cbor = json_to_cbor(&json);
1211-
let expected_cbor = serde_cbor::Value::Float(4.1);
1212-
assert_eq!(resulting_cbor, expected_cbor);
1174+
let resulting_radon = RadonTypes::try_from(json).unwrap();
1175+
let expected_radon = RadonFloat::from(4.1).into();
1176+
assert_eq!(resulting_radon, expected_radon);
12131177
}
12141178

12151179
#[test]
@@ -1227,30 +1191,15 @@ mod tests {
12271191
// This number is rounded to exactly 0.0 when converted to f64
12281192
assert_eq!(json.as_f64().unwrap(), 0.0);
12291193

1230-
// Convert to CBOR
1231-
let resulting_cbor = json_to_cbor(&json);
1194+
// Convert to RadonTypes
1195+
let resulting_radon = RadonTypes::try_from(json).unwrap();
12321196

12331197
// This exponent is too small to fit in a f64, so expected_f64 is equal to 0.0
12341198
let expected_f64 = 0.1E-99999;
12351199
assert_eq!(expected_f64, 0.0);
12361200
// And the expected CBOR value is a float, not an integer
1237-
let expected_cbor = serde_cbor::Value::Float(expected_f64);
1238-
assert_eq!(resulting_cbor, expected_cbor);
1239-
}
1240-
1241-
#[test]
1242-
fn test_json_numbers_to_cbor_booleans() {
1243-
use json::JsonValue;
1244-
1245-
let json = JsonValue::Boolean(false);
1246-
let resulting_cbor = json_to_cbor(&json);
1247-
let expected_cbor = serde_cbor::Value::Bool(false);
1248-
assert_eq!(resulting_cbor, expected_cbor);
1249-
1250-
let json = JsonValue::Boolean(true);
1251-
let resulting_cbor = json_to_cbor(&json);
1252-
let expected_cbor = serde_cbor::Value::Bool(true);
1253-
assert_eq!(resulting_cbor, expected_cbor);
1201+
let expected_radon = RadonFloat::from(expected_f64).into();
1202+
assert_eq!(resulting_radon, expected_radon);
12541203
}
12551204

12561205
#[test]

0 commit comments

Comments
 (0)