#![allow(clippy::needless_borrow)]
use std::str::FromStr;
use either::Either;
use num_bigint::BigInt;
use starlark_derive::starlark_module;
use crate as starlark;
use crate::collections::SmallMap;
use crate::environment::GlobalsBuilder;
use crate::typing::Ty;
use crate::values::dict::AllocDict;
use crate::values::type_repr::StarlarkTypeRepr;
use crate::values::types::int_or_big::StarlarkInt;
use crate::values::AllocFrozenValue;
use crate::values::AllocValue;
use crate::values::FrozenHeap;
use crate::values::FrozenValue;
use crate::values::Heap;
use crate::values::Value;
impl StarlarkTypeRepr for serde_json::Number {
fn starlark_type_repr() -> Ty {
Either::<i32, f64>::starlark_type_repr()
}
}
impl<'a> StarlarkTypeRepr for &'a serde_json::Number {
fn starlark_type_repr() -> Ty {
serde_json::Number::starlark_type_repr()
}
}
impl<'v, 'a> AllocValue<'v> for &'a serde_json::Number {
fn alloc_value(self, heap: &'v Heap) -> Value<'v> {
if let Some(x) = self.as_u64() {
heap.alloc(x)
} else if let Some(x) = self.as_i64() {
heap.alloc(x)
} else if let Some(x) = self.as_f64() {
heap.alloc(x)
} else if let Ok(x) = BigInt::from_str(&self.to_string()) {
heap.alloc(StarlarkInt::from(x))
} else {
panic!("Unrepresentable number: {:?}", self)
}
}
}
impl<'v> AllocValue<'v> for serde_json::Number {
fn alloc_value(self, heap: &'v Heap) -> Value<'v> {
#[allow(clippy::needless_borrows_for_generic_args)]
heap.alloc(&self)
}
}
impl<'a> AllocFrozenValue for &'a serde_json::Number {
fn alloc_frozen_value(self, heap: &FrozenHeap) -> FrozenValue {
if let Some(x) = self.as_u64() {
heap.alloc(x)
} else if let Some(x) = self.as_i64() {
heap.alloc(x)
} else if let Some(x) = self.as_f64() {
heap.alloc(x)
} else if let Ok(x) = BigInt::from_str(&self.to_string()) {
heap.alloc(StarlarkInt::from(x))
} else {
panic!("Unrepresentable number: {:?}", self)
}
}
}
impl AllocFrozenValue for serde_json::Number {
fn alloc_frozen_value(self, heap: &FrozenHeap) -> FrozenValue {
#[allow(clippy::needless_borrows_for_generic_args)]
heap.alloc(&self)
}
}
impl<'a, K: StarlarkTypeRepr, V: StarlarkTypeRepr> StarlarkTypeRepr for &'a serde_json::Map<K, V> {
fn starlark_type_repr() -> Ty {
AllocDict::<SmallMap<K, V>>::starlark_type_repr()
}
}
impl<K: StarlarkTypeRepr, V: StarlarkTypeRepr> StarlarkTypeRepr for serde_json::Map<K, V> {
fn starlark_type_repr() -> Ty {
AllocDict::<SmallMap<K, V>>::starlark_type_repr()
}
}
impl<'a, 'v> AllocValue<'v> for &'a serde_json::Map<String, serde_json::Value> {
fn alloc_value(self, heap: &'v Heap) -> Value<'v> {
heap.alloc(AllocDict(self))
}
}
impl<'v> AllocValue<'v> for serde_json::Map<String, serde_json::Value> {
fn alloc_value(self, heap: &'v Heap) -> Value<'v> {
#[allow(clippy::needless_borrows_for_generic_args)]
heap.alloc(&self)
}
}
impl<'a> AllocFrozenValue for &'a serde_json::Map<String, serde_json::Value> {
fn alloc_frozen_value(self, heap: &FrozenHeap) -> FrozenValue {
heap.alloc(AllocDict(self.iter().map(|(k, v)| (k.as_str(), v))))
}
}
impl AllocFrozenValue for serde_json::Map<String, serde_json::Value> {
fn alloc_frozen_value(self, heap: &FrozenHeap) -> FrozenValue {
#[allow(clippy::needless_borrows_for_generic_args)]
heap.alloc(&self)
}
}
impl<'a> StarlarkTypeRepr for &'a serde_json::Value {
fn starlark_type_repr() -> Ty {
Value::starlark_type_repr()
}
}
impl StarlarkTypeRepr for serde_json::Value {
fn starlark_type_repr() -> Ty {
Value::starlark_type_repr()
}
}
impl<'v, 'a> AllocValue<'v> for &'a serde_json::Value {
fn alloc_value(self, heap: &'v Heap) -> Value<'v> {
match self {
serde_json::Value::Null => Value::new_none(),
serde_json::Value::Bool(x) => Value::new_bool(*x),
serde_json::Value::Number(x) => heap.alloc(x),
serde_json::Value::String(x) => heap.alloc(x.as_str()),
serde_json::Value::Array(x) => heap.alloc(x.as_slice()),
serde_json::Value::Object(x) => heap.alloc(x),
}
}
}
impl<'v> AllocValue<'v> for serde_json::Value {
fn alloc_value(self, heap: &'v Heap) -> Value<'v> {
#[allow(clippy::needless_borrows_for_generic_args)]
heap.alloc(&self)
}
}
impl<'a> AllocFrozenValue for &'a serde_json::Value {
fn alloc_frozen_value(self, heap: &FrozenHeap) -> FrozenValue {
match self {
serde_json::Value::Null => FrozenValue::new_none(),
serde_json::Value::Bool(x) => FrozenValue::new_bool(*x),
serde_json::Value::Number(x) => heap.alloc(x),
serde_json::Value::String(x) => heap.alloc(x.as_str()),
serde_json::Value::Array(x) => heap.alloc(x.as_slice()),
serde_json::Value::Object(x) => heap.alloc(x),
}
}
}
impl AllocFrozenValue for serde_json::Value {
fn alloc_frozen_value(self, heap: &FrozenHeap) -> FrozenValue {
#[allow(clippy::needless_borrows_for_generic_args)]
heap.alloc(&self)
}
}
pub(crate) fn json(globals: &mut GlobalsBuilder) {
#[starlark_module]
fn json_members(globals: &mut GlobalsBuilder) {
fn encode(#[starlark(require = pos)] x: Value) -> anyhow::Result<String> {
x.to_json()
}
fn decode<'v>(
#[starlark(require = pos)] x: &str,
heap: &'v Heap,
) -> anyhow::Result<Value<'v>> {
Ok(heap.alloc(serde_json::from_str::<serde_json::Value>(x)?))
}
}
globals.struct_("json", json_members);
}
#[cfg(test)]
mod tests {
use crate::assert::Assert;
#[test]
fn test_json_encode() {
let a = Assert::new();
a.eq("'[10]'", "json.encode([10])");
}
#[test]
fn test_json_decode() {
let a = Assert::new();
a.eq(
"[10, None, False, {'k': 'v'}]",
"json.decode('[10, null, false, {\"k\": \"v\"}]')",
);
a.eq("3.142", "json.decode('3.142')");
a.eq(
"123456789123456789123456789",
"json.decode('123456789123456789123456789')",
);
}
}