/*! # FFI-functions for generating serde_json objects This module defines functions for generating opaque serde_json::Value objects in Ocaml which can then be deserialized with Serde on the Rust side. Signatures of the provided functions: ```Ocaml type serde_json = nativeint (* This stores pointers, so treat this as an opaque type! *) external build_null: unit -> serde_json = "rs_build_serde_null" external build_bool: bool -> serde_json = "rs_build_serde_bool" external build_number: int -> serde_json = "rs_build_serde_number" external build_string: string -> serde_json = "rs_build_serde_string" external build_array: serde_json list -> serde_json = "rs_build_serde_array_from_list" external build_object: (string * serde_json) list -> serde_json = "rs_build_serde_object" external build_bitvector: string -> serde_json = "rs_build_serde_bitvector" (* Convert a serde_json object to a json string (used for unit tests). *) external to_string: serde_json -> string = "rs_convert_json_to_string" ``` */ use super::OcamlSendable; use ocaml::{FromValue, ToValue}; use std::iter::FromIterator; use std::rc::Rc; use std::str::FromStr; use super::failwith_on_panic; /// A builder type for serde_json::Value objects. /// /// Hiding the recursive nature of the data type behind reference counts prevents unneccessary /// deep copies when creating json objects from Ocaml, which would lead to a runtime quadratic in the size of the json object. /// However, when converting to serde_json::Value, one deep copy is still necessary. #[derive(Clone, Debug)] pub enum JsonBuilder { Null, Bool(bool), Number(isize), PositiveNumber(u64), // currently used only for deserialization of bitvector String(String), Array(Vec<Rc<JsonBuilder>>), Object(Vec<(String, Rc<JsonBuilder>)>), } impl OcamlSendable for JsonBuilder {} /// Creating a serde_json::Value performing deep copy. impl From<&JsonBuilder> for serde_json::Value { fn from(builder: &JsonBuilder) -> serde_json::Value { match builder { JsonBuilder::Null => serde_json::Value::Null, JsonBuilder::Bool(val) => serde_json::Value::Bool(*val), JsonBuilder::Number(val) => serde_json::Value::Number(serde_json::Number::from(*val)), JsonBuilder::PositiveNumber(val) => { serde_json::Value::Number(serde_json::Number::from(*val)) } JsonBuilder::String(val) => serde_json::Value::String(val.to_string()), JsonBuilder::Array(elem_vec) => elem_vec .iter() .map(|rc_elem| serde_json::Value::from(&**rc_elem)) .collect(), JsonBuilder::Object(tuple_vec) => serde_json::Value::Object( serde_json::Map::from_iter(tuple_vec.iter().map(|(string_ref, json_builder)| { ( string_ref.to_string(), serde_json::Value::from(&**json_builder), ) })), ), } } } caml!(rs_finalize_json_builder(builder_val) { failwith_on_panic( || { JsonBuilder::ocaml_finalize(builder_val); ocaml::Value::unit() }) }); /// Build JsonBuilder::Null as Ocaml value fn build_serde_null() -> ocaml::Value { JsonBuilder::Null.to_ocaml() } caml!(rs_build_serde_null(_unit) { failwith_on_panic( || { build_serde_null() }) }); /// Build JsonBuilder::Bool as Ocaml value fn build_serde_bool(bool_val: ocaml::Value) -> ocaml::Value { let boolean: bool = bool::from_value(bool_val); JsonBuilder::Bool(boolean).to_ocaml() } caml!(rs_build_serde_bool(bool_val) { failwith_on_panic( || { build_serde_bool(bool_val) }) }); /// Build JsonBuilder::Number as Ocaml value fn build_serde_number(num: ocaml::Value) -> ocaml::Value { let num: isize = ocaml::Value::isize_val(&num); JsonBuilder::Number(num).to_ocaml() } caml!(rs_build_serde_number(number) { failwith_on_panic( || { build_serde_number(number) }) }); /// Build JsonBuilder::Object representing a bitvector from a string generated by `Bitvector.to_string` in Ocaml fn build_serde_bitvector(bitvector_string_val: ocaml::Value) -> ocaml::Value { let string = <&str>::from_value(bitvector_string_val); let elements: Vec<&str> = string.split(':').collect(); let width = usize::from_str(&elements[1][0..(elements[1].len() - 1)]) .expect("Bitvector width parsing failed"); assert!(width > 0); let mut num_list = Vec::new(); let mut number_slice: &str = elements[0]; if number_slice.starts_with("0x") { number_slice = &number_slice[2..]; } while !number_slice.is_empty() { if number_slice.len() > 16 { let digit = u64::from_str_radix(&number_slice[(number_slice.len() - 16)..], 16) .expect("Bitvector value parsing failed"); num_list.push(Rc::new(JsonBuilder::PositiveNumber(digit))); number_slice = &number_slice[..(number_slice.len() - 16)]; } else { let digit = u64::from_str_radix(&number_slice, 16).expect("Bitvector value parsing failed"); num_list.push(Rc::new(JsonBuilder::PositiveNumber(digit))); number_slice = ""; }; } while num_list.len() <= (width - 1) / 64 { num_list.push(Rc::new(JsonBuilder::PositiveNumber(0))); } num_list.reverse(); // since the digits were parsed in reverse order let mut width_list = Vec::new(); width_list.push(Rc::new(JsonBuilder::Number(width as isize))); let result = JsonBuilder::Object(vec![ ("digits".to_string(), Rc::new(JsonBuilder::Array(num_list))), ("width".to_string(), Rc::new(JsonBuilder::Array(width_list))), ]); result.to_ocaml() } caml!(rs_build_serde_bitvector(bitvector_string) { failwith_on_panic( || { build_serde_bitvector(bitvector_string) }) }); /// Build JsonBuilder::String as Ocaml value fn build_serde_string(string_val: ocaml::Value) -> ocaml::Value { let string = String::from_value(string_val); JsonBuilder::String(string).to_ocaml() } caml!(rs_build_serde_string(string_val) { failwith_on_panic( || { build_serde_string(string_val) }) }); /// Build JsonBuilder::Array as Ocaml value from an Ocaml list fn build_serde_array_from_list(list_val: ocaml::Value) -> ocaml::Value { let ocaml_list = ocaml::List::from(list_val); let value_vec = ocaml_list.to_vec(); let vec = value_vec .into_iter() .map(|ocaml_val| unsafe { JsonBuilder::from_ocaml_rc(&ocaml_val) }) .collect(); JsonBuilder::Array(vec).to_ocaml() } caml!(rs_build_serde_array_from_list(list_val) { failwith_on_panic( || { build_serde_array_from_list(list_val) }) }); /// Build JsonBuilder::Object as Ocaml value from an Ocaml list of tuples fn build_serde_object(tuple_list_val: ocaml::Value) -> ocaml::Value { let ocaml_list = ocaml::List::from(tuple_list_val); let pairs_vec = ocaml_list.to_vec(); let pairs = pairs_vec .into_iter() .map(|ocaml_tuple| { let tuple = ocaml::Tuple::from(ocaml_tuple); let key_ocaml = tuple .get(0) .expect("Error: Ocaml tuple contains no element"); let key = String::from_value(key_ocaml); let value_ocaml: ocaml::Value = tuple .get(1) .expect("Error: Ocaml tuple contains not enough elements"); let data = unsafe { JsonBuilder::from_ocaml_rc(&value_ocaml) }; (key, data) }) .collect(); JsonBuilder::Object(pairs).to_ocaml() } caml!(rs_build_serde_object(tuple_list_val) { failwith_on_panic( || { build_serde_object(tuple_list_val) }) }); /// Get the Json string corresponding to a JsonBuilder object and return it as an Ocaml value. fn get_json_string(builder_val: ocaml::Value) -> ocaml::Value { let builder = unsafe { JsonBuilder::from_ocaml(&builder_val) }; let json_string = serde_json::Value::from(builder).to_string(); ocaml::Str::from(&json_string as &str).to_value() } caml!(rs_convert_json_to_string(builder_val) { failwith_on_panic( || { get_json_string(builder_val) }) });