blob: 1db41af8a7ada0f047c2a05f5c00fef0ae82a724 [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.
*/
//! JSON values keep JSON Number semantics instead of serde_v8's automatic BigInt conversion.
use deno_core::{ToV8, v8};
use deno_error::JsErrorBox;
pub(super) use maka_plugin_core::json::encoded_size;
use maka_plugin_core::json::{exact_numbers, inexact_integer};
use serde_json::Value;
pub(super) struct Json(String);
impl Json {
pub(super) fn encode(value: &Value) -> Result<Self, JsErrorBox> {
exact_numbers(value).map_err(JsErrorBox::generic)?;
serde_json::to_string(value)
.map(Self)
.map_err(|e| JsErrorBox::generic(e.to_string()))
}
}
// V8's shortest decimal may spell a different mathematical integer while still
// identifying the same binary Number. Keep that Number, rather than minting an
// exact Rust integer from its approximate decimal spelling.
fn js_numbers(value: &mut Value) {
match value {
Value::Number(n) if inexact_integer(n) => {
*n = serde_json::Number::from_f64(n.as_f64().expect("finite JSON number"))
.expect("finite JSON number");
}
Value::Array(items) => items.iter_mut().for_each(js_numbers),
Value::Object(items) => items.values_mut().for_each(js_numbers),
_ => {}
}
}
impl<'a> ToV8<'a> for Json {
type Error = JsErrorBox;
fn to_v8<'i>(
self,
scope: &mut v8::PinScope<'a, 'i>,
) -> Result<v8::Local<'a, v8::Value>, Self::Error> {
let text = v8::String::new(scope, &self.0)
.ok_or_else(|| JsErrorBox::generic("JSON string allocation failed"))?;
v8::json::parse(scope, text).ok_or_else(|| JsErrorBox::generic("JSON parse terminated"))
}
}
pub(super) fn to_v8<'a, 'i>(
scope: &mut v8::PinScope<'a, 'i>,
value: &Value,
) -> Result<v8::Local<'a, v8::Value>, JsErrorBox> {
Json::encode(value)?.to_v8(scope)
}
pub(super) fn decode<'a, 'i>(
scope: &mut v8::PinScope<'a, 'i>,
value: v8::Local<'a, v8::Value>,
) -> Result<Value, JsErrorBox> {
v8::tc_scope!(scope, scope);
let value = if value.is_undefined() {
v8::null(scope).into()
} else {
value
};
let encoded = v8::json::stringify(scope, value).ok_or_else(|| {
JsErrorBox::generic(
scope
.exception()
.map(|e| e.to_rust_string_lossy(scope))
.unwrap_or_else(|| "value is not JSON".into()),
)
})?;
let mut value = serde_json::from_str(&encoded.to_rust_string_lossy(scope))
.map_err(|e| JsErrorBox::generic(e.to_string()))?;
js_numbers(&mut value);
Ok(value)
}