use crate::data::value::{DamlEnum, DamlRecord, DamlVariant};
use crate::data::{DamlError, DamlResult};
use crate::grpc_protobuf::com::daml::ledger::api::v2::value::Sum;
use crate::grpc_protobuf::com::daml::ledger::api::v2::{
Enum, GenMap, List, Optional, Record, TextMap, Value, Variant, gen_map, text_map,
};
use crate::util;
use crate::util::Required;
use std::convert::{TryFrom, TryInto};
use crate::nat::Nat;
use crate::primitive_types::{
DamlBool, DamlContractId, DamlDate, DamlFixedNumeric, DamlGenMap, DamlInt64, DamlList, DamlNumeric, DamlOptional,
DamlParty, DamlText, DamlTextMap, DamlTimestamp, DamlUnit,
};
use crate::serialize::{
DamlDeserializableType, DamlDeserializeFrom, DamlDeserializeInto, DamlSerializableType, DamlSerializeFrom,
DamlSerializeInto,
};
use itertools::Itertools;
use std::cmp::Ordering;
use std::str::FromStr;
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum DamlValue {
Record(DamlRecord),
Variant(DamlVariant),
Enum(DamlEnum),
ContractId(DamlContractId),
List(DamlList<DamlValue>),
Int64(DamlInt64),
Numeric(DamlNumeric),
Text(DamlText),
Timestamp(DamlTimestamp),
Party(DamlParty),
Bool(DamlBool),
Unit,
Date(DamlDate),
Optional(Option<Box<DamlValue>>),
TextMap(DamlTextMap<DamlValue>),
GenMap(DamlGenMap<DamlValue, DamlValue>),
}
impl DamlValue {
pub fn new_record(record: impl Into<DamlRecord>) -> Self {
DamlValue::Record(record.into())
}
pub fn new_variant(variant: impl Into<DamlVariant>) -> Self {
DamlValue::Variant(variant.into())
}
pub fn new_enum(enum_variant: impl Into<DamlEnum>) -> Self {
DamlValue::Enum(enum_variant.into())
}
pub fn new_contract_id(contract_id: impl Into<DamlContractId>) -> Self {
DamlValue::ContractId(contract_id.into())
}
pub fn new_list(list: impl Into<DamlList<Self>>) -> Self {
DamlValue::List(list.into())
}
pub fn new_int64(value: impl Into<DamlInt64>) -> Self {
DamlValue::Int64(value.into())
}
pub fn new_numeric(numeric: impl Into<DamlNumeric>) -> Self {
DamlValue::Numeric(numeric.into())
}
pub fn new_text(text: impl Into<DamlText>) -> Self {
DamlValue::Text(text.into())
}
pub fn new_timestamp(timestamp: impl Into<DamlTimestamp>) -> Self {
DamlValue::Timestamp(timestamp.into())
}
pub fn new_party(party: impl Into<DamlParty>) -> Self {
DamlValue::Party(party.into())
}
pub fn new_bool(value: DamlBool) -> Self {
DamlValue::Bool(value)
}
pub const fn new_unit() -> Self {
DamlValue::Unit
}
pub fn new_date(date: impl Into<DamlDate>) -> Self {
DamlValue::Date(date.into())
}
pub fn new_optional(optional: Option<Self>) -> Self {
DamlValue::Optional(optional.map(Box::new))
}
pub fn new_text_map(map: impl Into<DamlTextMap<Self>>) -> Self {
DamlValue::TextMap(map.into())
}
pub fn new_genmap(map: impl Into<DamlGenMap<Self, Self>>) -> Self {
DamlValue::GenMap(map.into())
}
pub fn try_unit(&self) -> DamlResult<()> {
match self {
DamlValue::Unit => Ok(()),
_ => Err(self.make_unexpected_type_error("Unit")),
}
}
pub fn try_unit_ref(&self) -> DamlResult<&()> {
match self {
DamlValue::Unit => Ok(&()),
_ => Err(self.make_unexpected_type_error("Unit")),
}
}
pub fn try_date(&self) -> DamlResult<DamlDate> {
match *self {
DamlValue::Date(d) => Ok(d),
_ => Err(self.make_unexpected_type_error("Date")),
}
}
pub fn try_date_ref(&self) -> DamlResult<&DamlDate> {
match self {
DamlValue::Date(d) => Ok(d),
_ => Err(self.make_unexpected_type_error("Date")),
}
}
pub fn try_int64(&self) -> DamlResult<DamlInt64> {
match self {
DamlValue::Int64(i) => Ok(*i),
_ => Err(self.make_unexpected_type_error("Int64")),
}
}
pub fn try_int64_ref(&self) -> DamlResult<&DamlInt64> {
match self {
DamlValue::Int64(i) => Ok(i),
_ => Err(self.make_unexpected_type_error("Int64")),
}
}
pub fn try_numeric(&self) -> DamlResult<&DamlNumeric> {
match self {
DamlValue::Numeric(d) => Ok(d),
_ => Err(self.make_unexpected_type_error("Numeric")),
}
}
pub fn try_numeric_clone(&self) -> DamlResult<DamlNumeric> {
match self {
DamlValue::Numeric(d) => Ok(d.clone()),
_ => Err(self.make_unexpected_type_error("Numeric")),
}
}
pub fn try_bool(&self) -> DamlResult<DamlBool> {
match self {
DamlValue::Bool(b) => Ok(*b),
_ => Err(self.make_unexpected_type_error("Bool")),
}
}
pub fn try_bool_ref(&self) -> DamlResult<&DamlBool> {
match self {
DamlValue::Bool(b) => Ok(b),
_ => Err(self.make_unexpected_type_error("Bool")),
}
}
pub fn try_text(&self) -> DamlResult<&DamlText> {
match self {
DamlValue::Text(s) => Ok(s),
_ => Err(self.make_unexpected_type_error("Text")),
}
}
pub fn try_timestamp(&self) -> DamlResult<DamlTimestamp> {
match *self {
DamlValue::Timestamp(ts) => Ok(ts),
_ => Err(self.make_unexpected_type_error("Timestamp")),
}
}
pub fn try_timestamp_ref(&self) -> DamlResult<&DamlTimestamp> {
match self {
DamlValue::Timestamp(ts) => Ok(ts),
_ => Err(self.make_unexpected_type_error("Timestamp")),
}
}
pub fn try_party(&self) -> DamlResult<&DamlParty> {
match self {
DamlValue::Party(party) => Ok(party),
_ => Err(self.make_unexpected_type_error("Party")),
}
}
pub fn try_contract_id(&self) -> DamlResult<&DamlContractId> {
match self {
DamlValue::ContractId(contract_id) => Ok(contract_id),
_ => Err(self.make_unexpected_type_error("ContractId")),
}
}
pub fn try_record(&self) -> DamlResult<&DamlRecord> {
match self {
DamlValue::Record(r) => Ok(r),
_ => Err(self.make_unexpected_type_error("Record")),
}
}
pub fn try_list(&self) -> DamlResult<&DamlList<Self>> {
match self {
DamlValue::List(l) => Ok(l),
_ => Err(self.make_unexpected_type_error("List")),
}
}
pub fn try_variant(&self) -> DamlResult<&DamlVariant> {
match self {
DamlValue::Variant(v) => Ok(v),
_ => Err(self.make_unexpected_type_error("Variant")),
}
}
pub fn try_enum(&self) -> DamlResult<&DamlEnum> {
match self {
DamlValue::Enum(e) => Ok(e),
_ => Err(self.make_unexpected_type_error("Enum")),
}
}
pub fn try_optional(&self) -> DamlResult<Option<&Self>> {
match self {
DamlValue::Optional(opt) => Ok(opt.as_deref()),
_ => Err(self.make_unexpected_type_error("Optional")),
}
}
pub fn try_text_map(&self) -> DamlResult<&DamlTextMap<Self>> {
match self {
DamlValue::TextMap(m) => Ok(m),
_ => Err(self.make_unexpected_type_error("TextMap")),
}
}
pub fn try_genmap(&self) -> DamlResult<&DamlGenMap<Self, Self>> {
match self {
DamlValue::GenMap(m) => Ok(m),
_ => Err(self.make_unexpected_type_error("GenMap")),
}
}
pub fn try_take_record(self) -> DamlResult<DamlRecord> {
match self {
DamlValue::Record(r) => Ok(r),
_ => Err(self.make_unexpected_type_error("Record")),
}
}
pub fn try_take_variant(self) -> DamlResult<DamlVariant> {
match self {
DamlValue::Variant(v) => Ok(v),
_ => Err(self.make_unexpected_type_error("Variant")),
}
}
pub fn try_take_enum(self) -> DamlResult<DamlEnum> {
match self {
DamlValue::Enum(e) => Ok(e),
_ => Err(self.make_unexpected_type_error("Enum")),
}
}
pub fn try_take_list(self) -> DamlResult<DamlList<Self>> {
match self {
DamlValue::List(l) => Ok(l),
_ => Err(self.make_unexpected_type_error("List")),
}
}
pub fn try_take_text_map(self) -> DamlResult<DamlTextMap<Self>> {
match self {
DamlValue::TextMap(m) => Ok(m),
_ => Err(self.make_unexpected_type_error("TextMap")),
}
}
pub fn try_take_genmap(self) -> DamlResult<DamlGenMap<Self, Self>> {
match self {
DamlValue::GenMap(m) => Ok(m),
_ => Err(self.make_unexpected_type_error("Map")),
}
}
pub fn try_take_optional(self) -> DamlResult<Option<Self>> {
match self {
DamlValue::Optional(o) => Ok(o.map(|b| *b)),
_ => Err(self.make_unexpected_type_error("Optional")),
}
}
pub fn variant_name(&self) -> &str {
match self {
DamlValue::Record(_) => "Record",
DamlValue::Variant(_) => "Variant",
DamlValue::Enum(_) => "Enum",
DamlValue::ContractId(_) => "ContractId",
DamlValue::List(_) => "List",
DamlValue::Int64(_) => "Int64",
DamlValue::Numeric(_) => "Numeric",
DamlValue::Text(_) => "Text",
DamlValue::Timestamp(_) => "Timestamp",
DamlValue::Party(_) => "Party",
DamlValue::Bool(_) => "Bool",
DamlValue::Unit => "Unit",
DamlValue::Date(_) => "Date",
DamlValue::Optional(_) => "Optional",
DamlValue::TextMap(_) => "TextMap",
DamlValue::GenMap(_) => "GenMap",
}
}
pub fn extract<'a, R, F>(&'a self, f: F) -> DamlResult<R>
where
F: FnOnce(&'a DamlRecord) -> DamlResult<R>,
{
f(self.try_record()?)
}
fn make_unexpected_type_error(&self, expected: &str) -> DamlError {
DamlError::UnexpectedType(expected.to_owned(), self.variant_name().to_owned())
}
fn ordinal(&self) -> u8 {
match self {
DamlValue::Unit => 0,
DamlValue::Bool(_) => 1,
DamlValue::Int64(_) => 2,
DamlValue::Numeric(_) => 3,
DamlValue::Text(_) => 4,
DamlValue::Party(_) => 5,
DamlValue::ContractId(_) => 6,
DamlValue::Timestamp(_) => 7,
DamlValue::Date(_) => 8,
DamlValue::Optional(_) => 9,
DamlValue::List(_) => 10,
DamlValue::TextMap(_) => 11,
DamlValue::GenMap(_) => 12,
DamlValue::Record(_) => 13,
DamlValue::Variant(_) => 14,
DamlValue::Enum(_) => 15,
}
}
}
impl From<()> for DamlValue {
fn from((): ()) -> Self {
Self::new_unit()
}
}
impl From<bool> for DamlValue {
fn from(b: bool) -> Self {
Self::new_bool(b)
}
}
impl From<&str> for DamlValue {
fn from(s: &str) -> Self {
Self::new_text(s)
}
}
impl From<String> for DamlValue {
fn from(s: String) -> Self {
Self::new_text(s)
}
}
impl From<u8> for DamlValue {
fn from(i: u8) -> Self {
Self::new_int64(i)
}
}
impl From<i8> for DamlValue {
fn from(i: i8) -> Self {
Self::new_int64(i)
}
}
impl From<u16> for DamlValue {
fn from(i: u16) -> Self {
Self::new_int64(i)
}
}
impl From<i16> for DamlValue {
fn from(i: i16) -> Self {
Self::new_int64(i)
}
}
impl From<u32> for DamlValue {
fn from(i: u32) -> Self {
Self::new_int64(i)
}
}
impl From<i32> for DamlValue {
fn from(i: i32) -> Self {
Self::new_int64(i)
}
}
impl From<i64> for DamlValue {
fn from(i: i64) -> Self {
Self::new_int64(i)
}
}
impl TryFrom<f32> for DamlValue {
type Error = DamlError;
fn try_from(d: f32) -> DamlResult<Self> {
Ok(Self::new_numeric(DamlNumeric::try_from(d)?))
}
}
impl TryFrom<f64> for DamlValue {
type Error = DamlError;
fn try_from(d: f64) -> DamlResult<Self> {
Ok(Self::new_numeric(DamlNumeric::try_from(d)?))
}
}
impl DamlSerializeFrom<DamlUnit> for DamlValue {
fn serialize_from((): DamlUnit) -> DamlValue {
Self::new_unit()
}
}
impl DamlSerializeFrom<DamlBool> for DamlValue {
fn serialize_from(b: DamlBool) -> DamlValue {
Self::new_bool(b)
}
}
impl DamlSerializeFrom<DamlInt64> for DamlValue {
fn serialize_from(i: DamlInt64) -> DamlValue {
Self::new_int64(i)
}
}
impl DamlSerializeFrom<DamlText> for DamlValue {
fn serialize_from(text: DamlText) -> DamlValue {
Self::new_text(text)
}
}
impl DamlSerializeFrom<DamlParty> for DamlValue {
fn serialize_from(party: DamlParty) -> DamlValue {
Self::new_party(party.party)
}
}
impl DamlSerializeFrom<DamlContractId> for DamlValue {
fn serialize_from(contract_id: DamlContractId) -> DamlValue {
Self::new_contract_id(contract_id.contract_id)
}
}
impl<T> DamlSerializeFrom<DamlFixedNumeric<T>> for DamlValue
where
T: DamlSerializableType + Nat,
{
fn serialize_from(numeric: DamlFixedNumeric<T>) -> DamlValue {
Self::new_numeric(numeric.value)
}
}
impl DamlSerializeFrom<DamlTimestamp> for DamlValue {
fn serialize_from(timestamp: DamlTimestamp) -> DamlValue {
Self::new_timestamp(timestamp)
}
}
impl DamlSerializeFrom<DamlDate> for DamlValue {
fn serialize_from(date: DamlDate) -> DamlValue {
Self::new_date(date)
}
}
impl<T> DamlSerializeFrom<Box<T>> for DamlValue
where
T: DamlSerializableType + DamlSerializeInto<DamlValue>,
{
fn serialize_from(boxed: Box<T>) -> DamlValue {
T::serialize_into(*boxed)
}
}
impl<T> DamlSerializeFrom<DamlOptional<T>> for DamlValue
where
T: DamlSerializableType + DamlSerializeInto<DamlValue>,
{
fn serialize_from(optional: DamlOptional<T>) -> DamlValue {
DamlValue::new_optional(optional.map(T::serialize_into))
}
}
impl<T> DamlSerializeFrom<DamlList<T>> for DamlValue
where
T: DamlSerializableType + DamlSerializeInto<DamlValue>,
{
fn serialize_from(list: DamlList<T>) -> DamlValue {
DamlValue::new_list(list.into_iter().map(T::serialize_into).collect::<DamlList<_>>())
}
}
impl<K, V> DamlSerializeFrom<DamlGenMap<K, V>> for DamlValue
where
K: DamlSerializableType + DamlSerializeInto<DamlValue>,
V: DamlSerializableType + DamlSerializeInto<DamlValue>,
{
fn serialize_from(gen_map: DamlGenMap<K, V>) -> DamlValue {
DamlValue::new_genmap(
gen_map
.into_iter()
.map(|(k, v)| (K::serialize_into(k), V::serialize_into(v)))
.collect::<DamlGenMap<_, _>>(),
)
}
}
impl<V> DamlSerializeFrom<DamlTextMap<V>> for DamlValue
where
V: DamlSerializableType + DamlSerializeInto<DamlValue>,
{
fn serialize_from(text_map: DamlTextMap<V>) -> DamlValue {
DamlValue::new_text_map(
text_map.0.into_iter().map(|(k, v)| (k, V::serialize_into(v))).collect::<DamlTextMap<_>>(),
)
}
}
impl DamlDeserializeFrom for DamlUnit {
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::Unit => Ok(()),
_ => Err(value.make_unexpected_type_error("Unit")),
}
}
}
impl DamlDeserializeFrom for DamlBool {
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::Bool(b) => Ok(b),
_ => Err(value.make_unexpected_type_error("Bool")),
}
}
}
impl DamlDeserializeFrom for DamlInt64 {
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::Int64(i) => Ok(i),
_ => Err(value.make_unexpected_type_error("Int64")),
}
}
}
impl DamlDeserializeFrom for DamlText {
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::Text(s) => Ok(s),
_ => Err(value.make_unexpected_type_error("Text")),
}
}
}
impl DamlDeserializeFrom for DamlParty {
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::Party(party) => Ok(party),
_ => Err(value.make_unexpected_type_error("Party")),
}
}
}
impl DamlDeserializeFrom for DamlContractId {
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::ContractId(contract_id) => Ok(contract_id),
_ => Err(value.make_unexpected_type_error("ContractId")),
}
}
}
impl<T> DamlDeserializeFrom for DamlFixedNumeric<T>
where
T: DamlDeserializableType + Nat,
{
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::Numeric(numeric) => Ok(DamlFixedNumeric::new(numeric)),
_ => Err(value.make_unexpected_type_error("Numeric")),
}
}
}
impl DamlDeserializeFrom for DamlTimestamp {
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::Timestamp(timestamp) => Ok(timestamp),
_ => Err(value.make_unexpected_type_error("Timestamp")),
}
}
}
impl DamlDeserializeFrom for DamlDate {
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::Date(date) => Ok(date),
_ => Err(value.make_unexpected_type_error("Date")),
}
}
}
impl<T> DamlDeserializeFrom for Box<T>
where
T: DamlDeserializeFrom + DamlDeserializableType,
{
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
Ok(Box::new(value.deserialize_into()?))
}
}
impl<T> DamlDeserializeFrom for DamlOptional<T>
where
T: DamlDeserializeFrom + DamlDeserializableType,
{
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::Optional(o) => Ok(o.map(|a| T::deserialize_from(*a)).transpose()?),
_ => Err(value.make_unexpected_type_error("Option")),
}
}
}
impl<T> DamlDeserializeFrom for DamlList<T>
where
T: DamlDeserializeFrom + DamlDeserializableType,
{
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::List(l) => Ok(l.into_iter().map(T::deserialize_from).collect::<DamlResult<DamlList<_>>>()?),
_ => Err(value.make_unexpected_type_error("List")),
}
}
}
impl<V> DamlDeserializeFrom for DamlTextMap<V>
where
V: DamlDeserializeFrom + DamlDeserializableType,
{
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::TextMap(text_map) => Ok(text_map
.into_iter()
.map(|(k, v)| Ok((k, V::deserialize_from(v)?)))
.collect::<DamlResult<DamlTextMap<_>>>()?),
_ => Err(value.make_unexpected_type_error("TextMap")),
}
}
}
impl<K, V> DamlDeserializeFrom for DamlGenMap<K, V>
where
K: DamlDeserializeFrom + DamlDeserializableType + Ord,
V: DamlDeserializeFrom + DamlDeserializableType,
{
fn deserialize_from(value: DamlValue) -> DamlResult<Self> {
match value {
DamlValue::GenMap(gen_map) => Ok(gen_map
.into_iter()
.map(|(k, v)| Ok((K::deserialize_from(k)?, V::deserialize_from(v)?)))
.collect::<DamlResult<DamlGenMap<_, _>>>()?),
_ => Err(value.make_unexpected_type_error("GenMap")),
}
}
}
impl TryFrom<Value> for DamlValue {
type Error = DamlError;
fn try_from(value: Value) -> Result<Self, Self::Error> {
Ok(match value.sum.req()? {
Sum::Record(v) => DamlValue::Record(v.try_into()?),
Sum::Variant(v) => DamlValue::Variant((*v).try_into()?),
Sum::Enum(e) => DamlValue::Enum(e.into()),
Sum::ContractId(v) => DamlValue::ContractId(DamlContractId::new(v)),
Sum::List(v) => {
DamlValue::List(v.elements.into_iter().map(TryInto::try_into).collect::<DamlResult<DamlList<_>>>()?)
},
Sum::Int64(v) => DamlValue::Int64(v),
Sum::Numeric(v) => DamlValue::Numeric(DamlNumeric::from_str(&v)?),
Sum::Text(v) => DamlValue::Text(v),
Sum::Timestamp(v) => DamlValue::Timestamp(util::datetime_from_micros(v)?),
Sum::Party(v) => DamlValue::Party(DamlParty::new(v)),
Sum::Bool(v) => DamlValue::Bool(v),
Sum::Unit(()) => DamlValue::Unit,
Sum::Date(v) => DamlValue::Date(util::date_from_days(v)?),
Sum::Optional(v) => {
DamlValue::Optional(v.value.map(|v| DamlValue::try_from(*v)).transpose()?.map(Box::new))
},
Sum::TextMap(v) => DamlValue::TextMap(
v.entries
.into_iter()
.map(|v| Ok((v.key, v.value.req().and_then(DamlValue::try_from)?)))
.collect::<DamlResult<DamlTextMap<_>>>()?,
),
Sum::GenMap(v) => DamlValue::GenMap(
v.entries
.into_iter()
.map(|v| {
Ok((v.key.req().and_then(DamlValue::try_from)?, v.value.req().and_then(DamlValue::try_from)?))
})
.collect::<DamlResult<DamlGenMap<_, _>>>()?,
),
})
}
}
impl From<DamlValue> for Value {
fn from(daml_value: DamlValue) -> Self {
Self {
sum: match daml_value {
DamlValue::Record(v) => Some(Sum::Record(Record::from(v))),
DamlValue::Variant(v) => Some(Sum::Variant(Box::new(Variant::from(v)))),
DamlValue::Enum(e) => Some(Sum::Enum(Enum::from(e))),
DamlValue::ContractId(v) => Some(Sum::ContractId(v.contract_id)),
DamlValue::List(v) => Some(Sum::List(List {
elements: v.into_iter().map(Value::from).collect(),
})),
DamlValue::Int64(v) => Some(Sum::Int64(v)),
DamlValue::Numeric(v) => Some(Sum::Numeric(v.to_plain_string())),
DamlValue::Text(v) => Some(Sum::Text(v)), DamlValue::Timestamp(v) => Some(Sum::Timestamp(v.timestamp())),
DamlValue::Party(v) => Some(Sum::Party(v.party)),
DamlValue::Bool(v) => Some(Sum::Bool(v)),
DamlValue::Unit => Some(Sum::Unit(())),
DamlValue::Date(v) => Some(Sum::Date(util::days_from_date(v))),
DamlValue::Optional(Some(v)) => Some(Sum::Optional(Box::new(Optional {
value: Some(Box::new(Value::from(*v))),
}))),
DamlValue::Optional(None) => Some(Sum::Optional(Box::new(Optional {
value: None,
}))),
DamlValue::TextMap(v) => Some(Sum::TextMap(TextMap {
entries: v
.into_iter()
.map(|(key, val)| text_map::Entry {
key,
value: Some(val.into()),
})
.collect(),
})),
DamlValue::GenMap(v) => Some(Sum::GenMap(GenMap {
entries: v
.into_iter()
.map(|(key, val)| gen_map::Entry {
key: Some(key.into()),
value: Some(val.into()),
})
.collect(),
})),
},
}
}
}
impl PartialOrd for DamlValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(DamlValue::Record(v1), DamlValue::Record(v2)) => v1.partial_cmp(v2),
(DamlValue::Variant(v1), DamlValue::Variant(v2)) => v1.partial_cmp(v2),
(DamlValue::Enum(v1), DamlValue::Enum(v2)) => v1.partial_cmp(v2),
(DamlValue::List(v1), DamlValue::List(v2)) => v1.partial_cmp(v2.as_ref()),
(DamlValue::Int64(v1), DamlValue::Int64(v2)) => v1.partial_cmp(v2),
(DamlValue::Numeric(v1), DamlValue::Numeric(v2)) => v1.partial_cmp(v2),
(DamlValue::Text(v1), DamlValue::Text(v2)) => v1.partial_cmp(v2),
(DamlValue::Party(v1), DamlValue::Party(v2)) => v1.party.partial_cmp(&v2.party),
(DamlValue::ContractId(v1), DamlValue::ContractId(v2)) => v1.contract_id.partial_cmp(&v2.contract_id),
(DamlValue::Timestamp(v1), DamlValue::Timestamp(v2)) => v1.partial_cmp(v2),
(DamlValue::Bool(v1), DamlValue::Bool(v2)) => v1.partial_cmp(v2),
(DamlValue::Unit, DamlValue::Unit) => Some(Ordering::Equal),
(DamlValue::Date(v1), DamlValue::Date(v2)) => v1.partial_cmp(v2),
(DamlValue::Optional(v1), DamlValue::Optional(v2)) => v1.partial_cmp(v2),
(DamlValue::TextMap(v1), DamlValue::TextMap(v2)) => {
if v1.len() == v2.len() {
let s1 = v1.iter().sorted_by(|(a, _), (b, _)| a.cmp(b));
let s2 = v2.iter().sorted_by(|(a, _), (b, _)| a.cmp(b));
s1.partial_cmp(s2)
} else {
v1.len().partial_cmp(&v2.len())
}
},
(DamlValue::GenMap(v1), DamlValue::GenMap(v2)) => {
if v1.len() == v2.len() {
v1.iter().partial_cmp(v2.iter())
} else {
v1.len().partial_cmp(&v2.len())
}
},
(a, b) => a.ordinal().partial_cmp(&b.ordinal()),
}
}
}
impl Ord for DamlValue {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).expect("DamlValue::partial_cmp is total")
}
}
#[cfg(test)]
mod tests {
use crate::data::value::DamlValue;
use crate::primitive_types::{DamlGenMap, DamlTextMap};
use std::cmp::Ordering;
#[test]
fn test_eq_for_text() {
let value1 = DamlValue::Text(String::from("text"));
let value2 = DamlValue::Text(String::from("text"));
assert_eq!(value1, value2);
}
#[test]
fn test_ord_for_text() {
let value1 = DamlValue::Text(String::from("textA"));
let value2 = DamlValue::Text(String::from("textB"));
assert_eq!(value1.cmp(&value2), Ordering::Less);
}
#[test]
fn test_eq_for_i64() {
let value1 = DamlValue::Int64(101);
let value2 = DamlValue::Int64(101);
assert_eq!(value1, value2);
}
#[test]
fn test_ord_for_i64() {
let value1 = DamlValue::Int64(102);
let value2 = DamlValue::Int64(101);
assert_eq!(value1.cmp(&value2), Ordering::Greater);
}
#[test]
fn test_eq_for_textmap() {
let items =
vec![(String::from("text1"), DamlValue::Int64(100)), (String::from("text2"), DamlValue::Int64(200))];
let value1 = DamlValue::TextMap(items.clone().into_iter().collect::<DamlTextMap<DamlValue>>());
let value2 = DamlValue::TextMap(items.into_iter().collect::<DamlTextMap<DamlValue>>());
assert_eq!(value1, value2);
}
#[test]
fn test_ord_for_textmap() {
let items1 =
vec![(String::from("text1"), DamlValue::Int64(100)), (String::from("text2"), DamlValue::Int64(200))];
let items2 =
vec![(String::from("text2"), DamlValue::Int64(100)), (String::from("text3"), DamlValue::Int64(200))];
let items3 =
vec![(String::from("text1"), DamlValue::Int64(100)), (String::from("text2"), DamlValue::Int64(200))];
let value1 = DamlValue::TextMap(items1.into_iter().collect::<DamlTextMap<DamlValue>>());
let value2 = DamlValue::TextMap(items2.into_iter().collect::<DamlTextMap<DamlValue>>());
let value3 = DamlValue::TextMap(items3.into_iter().collect::<DamlTextMap<DamlValue>>());
assert_eq!(value1.cmp(&value2), Ordering::Less);
assert_eq!(value2.cmp(&value1), Ordering::Greater);
assert_eq!(value1.cmp(&value3), Ordering::Equal);
}
#[test]
fn test_eq_for_genmap() {
let items = vec![
(DamlValue::Text(String::from("text1")), DamlValue::Int64(100)),
(DamlValue::Text(String::from("text2")), DamlValue::Int64(200)),
];
let value1 = DamlValue::GenMap(items.clone().into_iter().collect::<DamlGenMap<_, _>>());
let value2 = DamlValue::GenMap(items.into_iter().collect::<DamlGenMap<_, _>>());
assert_eq!(value1, value2);
}
#[test]
fn test_ord_for_genmap() {
let items1 = vec![
(DamlValue::Text(String::from("text1")), DamlValue::Int64(100)),
(DamlValue::Text(String::from("text2")), DamlValue::Int64(200)),
];
let items2 = vec![
(DamlValue::Text(String::from("text2")), DamlValue::Int64(100)),
(DamlValue::Text(String::from("text3")), DamlValue::Int64(200)),
];
let items3 = vec![
(DamlValue::Text(String::from("text1")), DamlValue::Int64(100)),
(DamlValue::Text(String::from("text2")), DamlValue::Int64(200)),
];
let value1 = DamlValue::GenMap(items1.into_iter().collect::<DamlGenMap<DamlValue, DamlValue>>());
let value2 = DamlValue::GenMap(items2.into_iter().collect::<DamlGenMap<DamlValue, DamlValue>>());
let value3 = DamlValue::GenMap(items3.into_iter().collect::<DamlGenMap<DamlValue, DamlValue>>());
assert_eq!(value1.cmp(&value2), Ordering::Less);
assert_eq!(value2.cmp(&value1), Ordering::Greater);
assert_eq!(value1.cmp(&value3), Ordering::Equal);
}
#[test]
fn test_ord_for_textmap_considers_values() {
let items1 = vec![(String::from("text1"), DamlValue::Int64(10)), (String::from("text2"), DamlValue::Int64(20))];
let items2 =
vec![(String::from("text1"), DamlValue::Int64(999)), (String::from("text2"), DamlValue::Int64(20))];
let value1 = DamlValue::TextMap(items1.into_iter().collect::<DamlTextMap<DamlValue>>());
let value2 = DamlValue::TextMap(items2.into_iter().collect::<DamlTextMap<DamlValue>>());
assert_ne!(value1, value2);
assert_eq!(Ordering::Less, value1.cmp(&value2));
}
#[test]
fn cross_variant_ord_does_not_panic() {
use std::collections::BTreeMap;
let a = DamlValue::Int64(1);
let b = DamlValue::Text("hi".to_owned());
let ord = a.cmp(&b);
assert_ne!(ord, Ordering::Equal);
assert_eq!(ord, b.cmp(&a).reverse(), "ordering must be antisymmetric across variants");
let mut m = BTreeMap::new();
m.insert(a, 1u32);
m.insert(b, 2u32);
assert_eq!(m.len(), 2);
}
#[test]
fn numeric_serialisation_preserves_natural_scale() {
use crate::grpc_protobuf::com::daml::ledger::api::v2::Value;
use crate::grpc_protobuf::com::daml::ledger::api::v2::value::Sum;
use bigdecimal::BigDecimal;
use std::str::FromStr;
let cases = ["0", "1.23", "0.00000000000000000000000000000000000001", "100", "-9.5"];
for s in cases {
let dv = DamlValue::Numeric(BigDecimal::from_str(s).unwrap());
let proto: Value = dv.into();
match proto.sum {
Some(Sum::Numeric(out)) => assert_eq!(out, s, "unexpected serialised form"),
other => panic!("expected Sum::Numeric, got {other:?}"),
}
}
}
#[test]
fn test_ord_for_genmap_considers_values() {
let items1 = vec![
(DamlValue::Text(String::from("text1")), DamlValue::Int64(10)),
(DamlValue::Text(String::from("text2")), DamlValue::Int64(20)),
];
let items2 = vec![
(DamlValue::Text(String::from("text1")), DamlValue::Int64(999)),
(DamlValue::Text(String::from("text2")), DamlValue::Int64(20)),
];
let value1 = DamlValue::GenMap(items1.into_iter().collect::<DamlGenMap<DamlValue, DamlValue>>());
let value2 = DamlValue::GenMap(items2.into_iter().collect::<DamlGenMap<DamlValue, DamlValue>>());
assert_ne!(value1, value2);
assert_eq!(Ordering::Less, value1.cmp(&value2));
}
}