blob: b903ffb527e673295ffeaed854061a7bee63f066 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use monty_types::{MontyObject, ObjectRef};
use serde_json::{Number, Value};
pub(crate) fn from_json(value: Value) -> MontyObject {
match value {
Value::Null => MontyObject::none(),
Value::Bool(value) => MontyObject::bool(value),
Value::String(value) => MontyObject::string(value),
Value::Number(value) => {
if let Some(value) = value.as_i64() {
MontyObject::int(value)
} else if let Some(value) = value.as_u64() {
MontyObject::bigint(value.into())
} else {
MontyObject::float(value.as_f64().expect("JSON number"))
}
}
Value::Array(values) => MontyObject::list(values.into_iter().map(from_json)),
Value::Object(values) => MontyObject::dict(
values
.into_iter()
.map(|(key, value)| (MontyObject::string(key), from_json(value))),
),
}
}
pub(crate) fn to_json(value: ObjectRef<'_>) -> Result<Value, String> {
convert(value, 0)
}
fn convert(value: ObjectRef<'_>, depth: usize) -> Result<Value, String> {
// Business JSON values have one depth bound, independent of protocol framing.
// Cycles also terminate here; they can never silently become repr strings.
if depth >= crate::protocol::MAX_JSON_DEPTH {
return Err("JSON value is cyclic or exceeds the transport nesting limit".into());
}
Ok(match value.type_name() {
"NoneType" => Value::Null,
"bool" => Value::Bool(value.as_bool().unwrap()),
"str" => Value::String(value.as_str().unwrap().into()),
"int" => {
let number = value
.py_repr()
.parse::<Number>()
.map_err(|_| "integer is outside the JSON number range")?;
if !number.is_i64() && !number.is_u64() {
return Err("integer is outside the JSON integer range".into());
}
Value::Number(number)
}
"float" => Value::Number(
Number::from_f64(value.as_float().unwrap()).ok_or("non-finite number is not JSON")?,
),
"list" | "tuple" => Value::Array(
value
.items()
.unwrap()
.into_iter()
.map(|item| convert(item, depth + 1))
.collect::<Result<_, _>>()?,
),
"dict" => {
let mut values = serde_json::Map::new();
for (key, item) in value.pairs().unwrap() {
let key = key.as_str().ok_or("JSON object keys must be strings")?;
values.insert(key.into(), convert(item, depth + 1)?);
}
Value::Object(values)
}
name => return Err(format!("{name} cannot cross the JSON boundary")),
})
}