use crate::error::{DamlJsonCodecError, DamlJsonCodecResult};
use crate::util::{AsSingleSliceExt, Required};
use chrono::{offset, Date, DateTime, NaiveDate};
use daml_grpc::data::value::{DamlEnum, DamlRecord, DamlRecordField, DamlValue, DamlVariant};
use daml_grpc::data::DamlIdentifier;
use daml_grpc::primitive_types::{DamlGenMap, DamlInt64, DamlNumeric, DamlTextMap};
use daml_lf::element;
use daml_lf::element::{DamlArchive, DamlData, DamlField, DamlType};
use serde_json::{Map, Value};
use std::convert::TryFrom;
use std::str::FromStr;
#[derive(Debug)]
pub struct JsonValueDecoder<'a> {
arc: &'a DamlArchive<'a>,
}
impl<'a> JsonValueDecoder<'a> {
pub const fn new(arc: &'a DamlArchive<'a>) -> Self {
Self {
arc,
}
}
pub fn decode(&self, json: &Value, ty: &DamlType<'_>) -> DamlJsonCodecResult<DamlValue> {
self.do_decode(json, ty, true)
}
fn do_decode(&self, json: &Value, ty: &DamlType<'_>, top_level: bool) -> DamlJsonCodecResult<DamlValue> {
match ty {
DamlType::Unit =>
if json.try_object()?.is_empty() {
Ok(DamlValue::Unit)
} else {
Err(DamlJsonCodecError::UnexpectedUnitData)
},
DamlType::Bool => Self::decode_bool(json),
DamlType::Int64 => Self::decode_int64(json),
DamlType::Text => Self::decode_text(json),
DamlType::Party => Self::decode_party(json),
DamlType::ContractId(_) => Self::decode_contract_id(json),
DamlType::Numeric(_) => Self::decode_numeric(json),
DamlType::Date => Self::decode_date(json),
DamlType::Timestamp => Self::decode_timestamp(json),
DamlType::List(tys) => Ok(DamlValue::List(
json.try_array()?
.iter()
.map(|item| self.do_decode(item, tys.as_single()?, true))
.collect::<DamlJsonCodecResult<Vec<_>>>()?,
)),
DamlType::TextMap(tys) => Ok(DamlValue::Map(
json.try_object()?
.iter()
.map(|(k, v)| Ok((k.clone(), self.do_decode(v, tys.as_single()?, true)?)))
.collect::<DamlJsonCodecResult<DamlTextMap<DamlValue>>>()?,
)),
DamlType::GenMap(tys) => {
let array = json.try_array()?;
let genmap = array
.iter()
.map(|item| match item.try_array()?.as_slice() {
[k, v] => Ok((
self.do_decode(k, tys.first().req()?, true)?,
self.do_decode(v, tys.last().req()?, true)?,
)),
_ => Err(DamlJsonCodecError::UnexpectedGenMapTypes),
})
.collect::<DamlJsonCodecResult<DamlGenMap<DamlValue, DamlValue>>>()?;
if array.len() == genmap.len() {
Ok(DamlValue::GenMap(genmap))
} else {
Err(DamlJsonCodecError::DuplicateGenMapKeys)
}
},
DamlType::TyCon(tycon) | DamlType::BoxedTyCon(tycon) => self.decode_data(
json,
self.arc
.data_by_tycon(tycon)
.ok_or_else(|| DamlJsonCodecError::DataNotFound(tycon.tycon().to_string()))?,
),
DamlType::Optional(nested) => {
let single = nested.as_single()?;
if top_level {
if json.is_null() {
Ok(DamlValue::Optional(None))
} else {
Ok(DamlValue::Optional(Some(Box::new(self.do_decode(json, single, false)?))))
}
} else {
match json.try_array()?.as_slice() {
[] => Ok(DamlValue::Optional(None)),
[inner_json] =>
Ok(DamlValue::Optional(Some(Box::new(self.do_decode(inner_json, single, false)?)))),
_ => Err(DamlJsonCodecError::UnexpectedOptionalArrayLength),
}
}
},
DamlType::Var(_)
| DamlType::Nat(_)
| DamlType::Arrow
| DamlType::Any
| DamlType::TypeRep
| DamlType::Bignumeric
| DamlType::RoundingMode
| DamlType::AnyException
| DamlType::Update
| DamlType::Scenario
| DamlType::Forall(_)
| DamlType::Struct(_)
| DamlType::Syn(_) => Err(DamlJsonCodecError::UnsupportedDamlType(ty.name().to_owned())),
}
}
fn decode_bool(json: &Value) -> DamlJsonCodecResult<DamlValue> {
Ok(DamlValue::Bool(json.try_bool()?))
}
fn decode_int64(json: &Value) -> DamlJsonCodecResult<DamlValue> {
match (json.as_i64(), json.as_str()) {
(Some(i64), None) => Ok(DamlValue::new_int64(i64)),
(None, Some(s)) => Ok(DamlValue::new_int64(DamlInt64::from_str(s)?)),
_ => Err(DamlJsonCodecError::UnexpectedJsonType(
"i64 or String".to_owned(),
Value::json_value_name(json).to_owned(),
)),
}
}
fn decode_numeric(json: &Value) -> DamlJsonCodecResult<DamlValue> {
match (json.as_f64(), json.as_str()) {
(Some(f64), None) => Ok(DamlValue::new_numeric(DamlNumeric::try_from(f64)?)),
(None, Some(s)) => Ok(DamlValue::new_numeric(DamlNumeric::from_str(s)?)),
_ => Err(DamlJsonCodecError::UnexpectedJsonType(
"f64 or String".to_owned(),
Value::json_value_name(json).to_owned(),
)),
}
}
fn decode_date(json: &Value) -> DamlJsonCodecResult<DamlValue> {
Ok(DamlValue::new_date(Date::from_utc(NaiveDate::parse_from_str(json.try_string()?, "%Y-%m-%d")?, offset::Utc)))
}
fn decode_timestamp(json: &Value) -> DamlJsonCodecResult<DamlValue> {
Ok(DamlValue::new_timestamp(DateTime::parse_from_rfc3339(json.try_string()?)?))
}
fn decode_text(json: &Value) -> DamlJsonCodecResult<DamlValue> {
Ok(DamlValue::new_text(json.try_string()?))
}
fn decode_party(json: &Value) -> DamlJsonCodecResult<DamlValue> {
Ok(DamlValue::new_party(json.try_string()?))
}
fn decode_contract_id(json: &Value) -> DamlJsonCodecResult<DamlValue> {
Ok(DamlValue::new_contract_id(json.try_string()?))
}
fn decode_data(&self, json: &Value, data: &DamlData<'_>) -> DamlJsonCodecResult<DamlValue> {
match data {
DamlData::Template(template) => self.decode_record(json, template.fields()),
DamlData::Record(record) => self.decode_record(json, record.fields()),
DamlData::Variant(variant) => self.decode_variant(json, variant.fields()),
DamlData::Enum(data_enum) => Self::decode_enum(json, data_enum),
}
}
fn decode_enum(json: &Value, data_enum: &element::DamlEnum<'_>) -> DamlJsonCodecResult<DamlValue> {
let constructor = json.try_string()?;
if data_enum.constructors().any(|c| c == constructor) {
Ok(DamlValue::Enum(DamlEnum::new(constructor, None::<DamlIdentifier>)))
} else {
Err(DamlJsonCodecError::UnknownEnumConstructor(constructor.to_owned()))
}
}
fn decode_variant(&self, json: &Value, constructors: &[DamlField<'_>]) -> DamlJsonCodecResult<DamlValue> {
let object = json.try_object()?;
let tag = object.get("tag").req()?.try_string()?;
let value = object.get("value").req()?;
let constructor = constructors
.iter()
.find(|&field| field.name() == tag)
.ok_or_else(|| DamlJsonCodecError::UnknownVariantConstructor(tag.to_owned()))?;
Ok(DamlValue::Variant(DamlVariant::new(
constructor.name(),
Box::new(self.decode(value, constructor.ty())?),
None::<DamlIdentifier>,
)))
}
fn decode_record(&self, json: &Value, lf_fields: &[DamlField<'_>]) -> DamlJsonCodecResult<DamlValue> {
let fields = match (json.as_object(), json.as_array()) {
(Some(obj), None) => self.decode_record_object(obj, lf_fields)?,
(None, Some(arr)) => self.decode_record_array(arr, lf_fields)?,
_ =>
return Err(DamlJsonCodecError::UnexpectedJsonType(
"Object or Array".to_owned(),
Value::json_value_name(json).to_owned(),
)),
};
Ok(DamlValue::Record(DamlRecord::new(fields, None::<DamlIdentifier>)))
}
fn decode_record_object(
&self,
obj: &Map<String, Value>,
lf_fields: &[DamlField<'_>],
) -> DamlJsonCodecResult<Vec<DamlRecordField>> {
lf_fields
.iter()
.map(|field| {
let field_name = field.name();
let field_ty = field.ty();
let field_json = obj.get(field_name);
match (field_ty, field_json) {
(DamlType::Optional(_), None) =>
Ok(DamlRecordField::new(Some(field_name), DamlValue::Optional(None))),
(_, Some(json)) => Ok(DamlRecordField::new(Some(field_name), self.decode(json, field_ty)?)),
(_, None) => Err(DamlJsonCodecError::MissingJsonRecordObjectField(field_name.to_owned())),
}
})
.collect::<DamlJsonCodecResult<Vec<DamlRecordField>>>()
}
fn decode_record_array(
&self,
arr: &[Value],
lf_fields: &[DamlField<'_>],
) -> DamlJsonCodecResult<Vec<DamlRecordField>> {
lf_fields
.iter()
.enumerate()
.map(|(i, field)| {
let json = arr
.get(i)
.ok_or_else(|| DamlJsonCodecError::MissingJsonRecordArrayField(i, field.name().to_owned()))?;
Ok(DamlRecordField::new(Some(field.name()), self.decode(json, field.ty())?))
})
.collect::<DamlJsonCodecResult<Vec<DamlRecordField>>>()
}
}
pub trait JsonTryAsExt {
fn try_null(&self) -> DamlJsonCodecResult<()>;
fn try_bool(&self) -> DamlJsonCodecResult<bool>;
fn try_int64(&self) -> DamlJsonCodecResult<i64>;
fn try_string(&self) -> DamlJsonCodecResult<&str>;
fn try_array(&self) -> DamlJsonCodecResult<&Vec<Value>>;
fn try_object(&self) -> DamlJsonCodecResult<&Map<String, Value>>;
fn make_unexpected_type_error(value: &Value, expected: &str) -> DamlJsonCodecError {
DamlJsonCodecError::UnexpectedJsonType(expected.to_owned(), Self::json_value_name(value).to_owned())
}
fn json_value_name(value: &Value) -> &str {
match value {
Value::Null => "Null",
Value::Bool(_) => "Bool",
Value::Number(_) => "Number",
Value::String(_) => "String",
Value::Array(_) => "Array",
Value::Object(_) => "Object",
}
}
}
impl JsonTryAsExt for Value {
fn try_null(&self) -> DamlJsonCodecResult<()> {
self.as_null().ok_or_else(|| Self::make_unexpected_type_error(self, "Null"))
}
fn try_bool(&self) -> DamlJsonCodecResult<bool> {
self.as_bool().ok_or_else(|| Self::make_unexpected_type_error(self, "Bool"))
}
fn try_int64(&self) -> DamlJsonCodecResult<i64> {
self.as_i64().ok_or_else(|| Self::make_unexpected_type_error(self, "Number(i64)"))
}
fn try_string(&self) -> DamlJsonCodecResult<&str> {
self.as_str().ok_or_else(|| Self::make_unexpected_type_error(self, "String"))
}
fn try_array(&self) -> DamlJsonCodecResult<&Vec<Value>> {
self.as_array().ok_or_else(|| Self::make_unexpected_type_error(self, "Array"))
}
fn try_object(&self) -> DamlJsonCodecResult<&Map<String, Value>> {
self.as_object().ok_or_else(|| Self::make_unexpected_type_error(self, "Object"))
}
}
#[cfg(test)]
mod tests {
use super::{
DamlArchive, DamlEnum, DamlJsonCodecError, DamlJsonCodecResult, DamlType, DamlValue, JsonValueDecoder, Value,
};
use daml::macros::daml_value;
use daml_grpc::primitive_types::DamlTextMap;
use daml_lf::DarFile;
use maplit::hashmap;
use serde_json::json;
static TESTING_TYPES_DAR_PATH: &str = "../resources/testing_types_sandbox/TestingTypes-latest.dar";
#[test]
fn test_unit() -> DamlJsonCodecResult<()> {
let json_value = json!({});
let ty = DamlType::Unit;
let expected = daml_value!();
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_bool() -> DamlJsonCodecResult<()> {
let json_value = json!(true);
let ty = DamlType::Bool;
let expected = daml_value!(true);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_int64() -> DamlJsonCodecResult<()> {
let json_value = json!(42);
let ty = DamlType::Int64;
let expected = daml_value!(42);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_int64_neg() -> DamlJsonCodecResult<()> {
let json_value = json!(-42);
let ty = DamlType::Int64;
let expected = daml_value!(-42);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_int64_string() -> DamlJsonCodecResult<()> {
let json_value = json!("42");
let ty = DamlType::Int64;
let expected = daml_value!(42);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_int64_neg_string() -> DamlJsonCodecResult<()> {
let json_value = json!("-42");
let ty = DamlType::Int64;
let expected = daml_value!(-42);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_int64_fails() {
let json_value = json!(4.2);
let ty = DamlType::Int64;
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty);
assert!(actual.is_err());
}
#[test]
fn test_numeric_f64() -> DamlJsonCodecResult<()> {
let json_value = json!(1.0);
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let expected = daml_value!(1.0);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_numeric_f64_neg() -> DamlJsonCodecResult<()> {
let json_value = json!(-1.0);
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let expected = daml_value!(-1.0);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_numeric_string() -> DamlJsonCodecResult<()> {
let json_value = json!("1.23");
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let expected = daml_value!(1.23);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_numeric_string_neg() -> DamlJsonCodecResult<()> {
let json_value = json!("-1.23");
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let expected = daml_value!(-1.23);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_numeric_f64_round() -> DamlJsonCodecResult<()> {
let json_value = json!(0.30000000000000004);
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let expected = daml_value!(0.3);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_numeric_f64_sci() -> DamlJsonCodecResult<()> {
let json_value = json!(2e3);
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let expected = daml_value!(2000.0);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_numeric_f64_neg_zero() -> DamlJsonCodecResult<()> {
let json_value = json!(-0);
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let expected = daml_value!(0.0);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_numeric_f64_large() -> DamlJsonCodecResult<()> {
let json_value = json!(9999999999999999999999999999.9999999999);
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let expected = daml_value!(9999999999999999999999999999.9999999999);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_numeric_f64_whole() -> DamlJsonCodecResult<()> {
let json_value = json!(42);
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let expected = daml_value!(42.0);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_numeric_string_whole() -> DamlJsonCodecResult<()> {
let json_value = json!("42");
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let expected = daml_value!(42.0);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_numeric_string_fails_garbage() {
let json_value = json!("blah");
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty);
assert!(actual.is_err());
}
#[test]
fn test_numeric_string_fails_whitespace() {
let json_value = json!(" 42 ");
let ty = DamlType::Numeric(vec![DamlType::Nat(10)]);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty);
assert!(actual.is_err());
}
#[test]
fn test_text() -> DamlJsonCodecResult<()> {
let json_value = json!("test");
let ty = DamlType::Text;
let expected = daml_value!("test");
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_text_empty() -> DamlJsonCodecResult<()> {
let json_value = json!("");
let ty = DamlType::Text;
let expected = daml_value!("");
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_date() -> DamlJsonCodecResult<()> {
let json_value = json!("2019-06-18");
let ty = DamlType::Date;
let expected = daml_value!("2019-06-18"::d);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_date_min() -> DamlJsonCodecResult<()> {
let json_value = json!("9999-12-31");
let ty = DamlType::Date;
let expected = daml_value!("9999-12-31"::d);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_date_max() -> DamlJsonCodecResult<()> {
let json_value = json!("0001-01-01");
let ty = DamlType::Date;
let expected = daml_value!("0001-01-01"::d);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_date_invalid_fails() {
let json_value = json!("9999-99-99");
let ty = DamlType::Date;
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty);
assert!(actual.is_err());
}
#[test]
fn test_timestamp_full() -> DamlJsonCodecResult<()> {
let json_value = json!("1990-11-09T04:30:23.1234569Z");
let ty = DamlType::Timestamp;
let expected = daml_value!("1990-11-09T04:30:23.1234569Z"::t);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_timestamp_no_sub_sec() -> DamlJsonCodecResult<()> {
let json_value = json!("1990-11-09T04:30:23Z");
let ty = DamlType::Timestamp;
let expected = daml_value!("1990-11-09T04:30:23Z"::t);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_timestamp_no_micros() -> DamlJsonCodecResult<()> {
let json_value = json!("1990-11-09T04:30:23.123Z");
let ty = DamlType::Timestamp;
let expected = daml_value!("1990-11-09T04:30:23.123Z"::t);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_timestamp_min() -> DamlJsonCodecResult<()> {
let json_value = json!("0001-01-01T00:00:00Z");
let ty = DamlType::Timestamp;
let expected = daml_value!("0001-01-01T00:00:00Z"::t);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_timestamp_max() -> DamlJsonCodecResult<()> {
let json_value = json!("9999-12-31T23:59:59.999999Z");
let ty = DamlType::Timestamp;
let expected = daml_value!("9999-12-31T23:59:59.999999Z"::t);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_party() -> DamlJsonCodecResult<()> {
let json_value = json!("Alice");
let ty = DamlType::Party;
let expected = daml_value!("Alice"::p);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_contract_id() -> DamlJsonCodecResult<()> {
let json_value = json!("foo:bar#baz");
let ty = DamlType::ContractId(None);
let expected = daml_value!("foo:bar#baz"::c);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_int_null() -> DamlJsonCodecResult<()> {
let json_value = json!(null);
let ty = DamlType::Optional(vec![DamlType::Int64]);
let expected = daml_value!({?!});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_int_null_fails() {
let json_value = json!([]);
let ty = DamlType::Optional(vec![DamlType::Int64]);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty);
assert!(actual.is_err());
}
#[test]
fn test_opt_opt_int_some_should_fail() {
let json_value = json!([null]);
let ty = DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Int64])]);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty);
assert!(actual.is_err());
}
#[test]
fn test_opt_opt_int_null() -> DamlJsonCodecResult<()> {
let json_value = json!(null);
let ty = DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Int64])]);
let expected = daml_value!({?!});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_int_some() -> DamlJsonCodecResult<()> {
let json_value = json!(42);
let ty = DamlType::Optional(vec![DamlType::Int64]);
let expected = daml_value!({?=42});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_opt_int_some_none() -> DamlJsonCodecResult<()> {
let json_value = json!([]);
let ty = DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Int64])]);
let expected = daml_value!({?={?!}});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_opt_int_some_some() -> DamlJsonCodecResult<()> {
let json_value = json!([42]);
let ty = DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Int64])]);
let expected = daml_value!({?={?=42}});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_opt_opt_int_some_some_none() -> DamlJsonCodecResult<()> {
let json_value = json!([[]]);
let ty = DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Int64])])]);
let expected = daml_value!({?={?={?!}}});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_opt_opt_int_some_some_some() -> DamlJsonCodecResult<()> {
let json_value = json!([[42]]);
let ty = DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Int64])])]);
let expected = daml_value!({?={?={?=42}}});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_opt_opt_opt_int_some_some_some_none() -> DamlJsonCodecResult<()> {
let json_value = json!([[[]]]);
let ty =
DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Optional(vec![
DamlType::Int64,
])])])]);
let expected = daml_value!({?={?={?={?!}}}});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_opt_opt_opt_int_some_some_some_some() -> DamlJsonCodecResult<()> {
let json_value = json!([[[42]]]);
let ty =
DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Optional(vec![DamlType::Optional(vec![
DamlType::Int64,
])])])]);
let expected = daml_value!({?={?={?={?=42}}}});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_unit_null() -> DamlJsonCodecResult<()> {
let json_value = json!(null);
let ty = DamlType::Optional(vec![DamlType::Unit]);
let expected = daml_value!({?!});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_opt_unit_some() -> DamlJsonCodecResult<()> {
let json_value = json!({});
let ty = DamlType::Optional(vec![DamlType::Unit]);
let expected = daml_value!({?=()});
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_record() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({
"landlord": "Alice",
"tenant": "Bob",
"terms": "test terms",
});
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "RentDemo"], "RentalAgreement");
let expected = daml_value!({landlord: "Alice"::p, tenant: "Bob"::p, terms: "test terms"});
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_record_array() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!(["Alice", "Bob", 0]);
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "PingPong"], "Ping");
let expected = daml_value!({sender: "Alice"::p, receiver: "Bob"::p, count: 0});
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_record_depth1_omitted() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({});
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Depth1");
let expected = daml_value!({foo: {?!}});
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_record_depth1_none() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({ "foo": null });
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Depth1");
let expected = daml_value!({foo: {?!}});
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_record_depth1_some() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({ "foo": 42 });
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Depth1");
let expected = daml_value!({foo: {?=42}});
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_record_depth2_omitted() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({});
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Depth2");
let expected = daml_value!({foo: {?!}});
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_record_depth2_none() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({ "foo": null });
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Depth2");
let expected = daml_value!({foo: {?!}});
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_record_depth2_some_none() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({ "foo": [] });
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Depth2");
let expected = daml_value!({foo: {?={?!}}});
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_record_depth2_some_some() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({ "foo": [42] });
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Depth2");
let expected = daml_value!({foo: {?={?=42}}});
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_list_bool_empty() -> DamlJsonCodecResult<()> {
let json_value = json!([]);
let ty = DamlType::List(vec![DamlType::Bool]);
let expected = daml_value!([]);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_list_text() -> DamlJsonCodecResult<()> {
let json_value = json!(["a", "b", "c"]);
let ty = DamlType::List(vec![DamlType::Text]);
let expected = daml_value!(["a", "b", "c"]);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_list_opt_text() -> DamlJsonCodecResult<()> {
let json_value = json!(["a", null, "c"]);
let ty = DamlType::List(vec![DamlType::Optional(vec![DamlType::Text])]);
let expected = daml_value!([{?="a"}, {?!}, {?="c"}]);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_list_opt_mixed_fails() {
let json_value = json!([42, null, "c"]);
let ty = DamlType::List(vec![DamlType::Optional(vec![DamlType::Text])]);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty);
assert!(actual.is_err());
}
#[test]
fn test_list_record() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!([{
"landlord": "Alice",
"tenant": "Bob",
"terms": "test terms",
},
{
"landlord": "John",
"tenant": "Paul",
"terms": "more test terms",
}]);
let ty = DamlType::List(vec![DamlType::make_tycon(&dar.main.hash, &["Fuji", "RentDemo"], "RentalAgreement")]);
let expected = daml_value!([{
landlord: "Alice"::p,
tenant: "Bob"::p,
terms: "test terms"
},
{
landlord: "John"::p,
tenant: "Paul"::p,
terms: "more test terms"
}]);
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_textmap_int_empty() -> DamlJsonCodecResult<()> {
let json_value = json!({});
let ty = DamlType::TextMap(vec![DamlType::Int64]);
let expected = DamlValue::Map(vec![].into_iter().collect());
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_textmap_int() -> DamlJsonCodecResult<()> {
let json_value = json!({"foo": 42, "bar": 43});
let ty = DamlType::TextMap(vec![DamlType::Int64]);
let expected = DamlValue::Map(
vec![("foo".to_owned(), daml_value![42]), ("bar".to_owned(), daml_value![43])].into_iter().collect(),
);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_list_textmap_int() -> DamlJsonCodecResult<()> {
let json_value = json!([{"foo": 42}, {"bar": 43}]);
let ty = DamlType::List(vec![DamlType::TextMap(vec![DamlType::Int64])]);
let expected = daml_value![[
(DamlValue::Map(DamlTextMap::from(hashmap! {"foo".to_owned() => daml_value![42]}))),
(DamlValue::Map(DamlTextMap::from(hashmap! {"bar".to_owned() => daml_value![43]})))
]];
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_textmap_list_int() -> DamlJsonCodecResult<()> {
let json_value = json!({"foo": [1, 2, 3], "bar": [4, 5, 6]});
let ty = DamlType::TextMap(vec![DamlType::List(vec![DamlType::Int64])]);
let expected = DamlValue::Map(DamlTextMap::from(hashmap! {
"foo".to_owned() => daml_value![[1, 2, 3]],
"bar".to_owned() => daml_value![[4, 5, 6]]
}));
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_textmap_record() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({
"first": {
"landlord": "Alice",
"tenant": "Bob",
"terms": "test terms",
},
"last": {
"landlord": "John",
"tenant": "Paul",
"terms": "more test terms",
}});
let ty =
DamlType::TextMap(vec![DamlType::make_tycon(&dar.main.hash, &["Fuji", "RentDemo"], "RentalAgreement")]);
let expected = DamlValue::Map(DamlTextMap::from(hashmap! {
"first".to_owned() => daml_value!({landlord: "Alice"::p, tenant: "Bob"::p, terms: "test terms"}),
"last".to_owned() => daml_value!({landlord: "John"::p, tenant: "Paul"::p, terms: "more test terms"})
}));
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_genmap_int_empty() -> DamlJsonCodecResult<()> {
let json_value = json!([]);
let ty = DamlType::GenMap(vec![DamlType::Int64]);
let expected = DamlValue::GenMap(vec![].into_iter().collect());
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_genmap_string_to_int() -> DamlJsonCodecResult<()> {
let json_value = json!([["foo", 42], ["bar", 43]]);
let ty = DamlType::GenMap(vec![DamlType::Text, DamlType::Int64]);
let expected = DamlValue::GenMap(
vec![(daml_value!["foo"], daml_value![42]), (daml_value!["bar"], daml_value![43])].into_iter().collect(),
);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_genmap_int_to_string() -> DamlJsonCodecResult<()> {
let json_value = json!([[42, "foo"], [43, "bar"]]);
let ty = DamlType::GenMap(vec![DamlType::Int64, DamlType::Text]);
let expected = DamlValue::GenMap(
vec![(daml_value![42], daml_value!["foo"]), (daml_value![43], daml_value!["bar"])].into_iter().collect(),
);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_genmap_person_to_string() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!([[{"name": "Alice", "age": 30}, "foo"], [{"name": "Bob", "age": 18}, "bar"]]);
let key_type = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Person");
let value_type = DamlType::Text;
let ty = DamlType::GenMap(vec![key_type, value_type]);
let expected = DamlValue::GenMap(
vec![
(daml_value![{name: "Alice", age: 30}], daml_value!["foo"]),
(daml_value![{name: "Bob", age: 18}], daml_value!["bar"]),
]
.into_iter()
.collect(),
);
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_genmap_duplicate_key_should_fail() {
let json_value = json!([[42, "foo"], [42, "bar"]]);
let ty = DamlType::GenMap(vec![DamlType::Int64, DamlType::Text]);
let actual = JsonValueDecoder::new(&DamlArchive::default()).decode(&json_value, &ty);
assert!(actual.is_err());
}
#[test]
fn test_variant_bar() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({"tag": "Bar", "value": 42});
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Foo");
let expected = daml_value![{=>Bar 42}];
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_variant_baz() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({"tag": "Baz", "value": {}});
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Foo");
let expected = daml_value![{=>Baz}];
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_variant_quux_none() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({"tag": "Quux", "value": null});
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Foo");
let expected = daml_value![{=>Quux {?!}}];
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_variant_quux_some() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!({"tag": "Quux", "value": 42});
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Foo");
let expected = daml_value![{=>Quux {?=42}}];
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_enum_enabled() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!("Enabled");
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Status");
let expected = DamlValue::Enum(DamlEnum::new("Enabled", None));
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_enum_disabled() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!("Disabled");
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Status");
let expected = DamlValue::Enum(DamlEnum::new("Disabled", None));
let actual = decode_apply(&dar, &json_value, &ty)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn test_enum_unknown_error() -> DamlJsonCodecResult<()> {
let dar = DarFile::from_file(TESTING_TYPES_DAR_PATH)?;
let json_value = json!("Unknown");
let ty = DamlType::make_tycon(&dar.main.hash, &["Fuji", "JsonTest"], "Status");
let actual = decode_apply(&dar, &json_value, &ty);
assert!(actual.is_err());
Ok(())
}
fn decode_apply(dar: &DarFile, json_value: &Value, ty: &DamlType<'_>) -> DamlJsonCodecResult<DamlValue> {
dar.apply(|arc| {
let decoded_value = JsonValueDecoder::new(arc).decode(json_value, ty)?;
Ok::<DamlValue, DamlJsonCodecError>(decoded_value)
})?
}
}