use crate::data::DamlError;
use crate::nat::{Nat, Nat10};
use bigdecimal::BigDecimal;
use chrono::{Date, DateTime, Utc};
use itertools::Itertools;
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap};
use std::convert::TryFrom;
use std::fmt::Formatter;
use std::iter::FromIterator;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
pub type DamlInt64 = i64;
pub type DamlNumeric = BigDecimal;
pub type DamlNumeric10 = DamlFixedNumeric<Nat10>;
pub type DamlText = String;
pub type DamlTimestamp = DateTime<Utc>;
pub type DamlBool = bool;
pub type DamlUnit = ();
pub type DamlDate = Date<Utc>;
pub type DamlList<T> = Vec<T>;
pub type DamlTextMap<V> = DamlTextMapImpl<V>;
pub type DamlGenMap<K, V> = BTreeMap<K, V>;
pub type DamlOptional<T> = Option<T>;
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone)]
pub struct DamlParty {
pub party: String,
}
impl DamlParty {
pub fn new(party: impl Into<String>) -> Self {
Self {
party: party.into(),
}
}
pub fn as_str(&self) -> &str {
self.party.as_str()
}
}
impl From<&str> for DamlParty {
fn from(party: &str) -> Self {
DamlParty::new(party)
}
}
impl From<String> for DamlParty {
fn from(party: String) -> Self {
DamlParty::new(party)
}
}
impl PartialEq<&DamlParty> for &str {
fn eq(&self, other: &&DamlParty) -> bool {
*self == other.party
}
}
impl PartialEq<&str> for &DamlParty {
fn eq(&self, other: &&str) -> bool {
self.party == *other
}
}
impl PartialEq<DamlParty> for &str {
fn eq(&self, other: &DamlParty) -> bool {
self == &other.party
}
}
impl PartialEq<&str> for DamlParty {
fn eq(&self, other: &&str) -> bool {
&self.party == other
}
}
impl std::fmt::Display for DamlParty {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.party.fmt(f)
}
}
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone)]
pub struct DamlContractId {
pub contract_id: String,
}
impl DamlContractId {
pub fn new(contract_id: impl Into<String>) -> Self {
Self {
contract_id: contract_id.into(),
}
}
pub fn as_str(&self) -> &str {
self.contract_id.as_str()
}
}
impl From<&str> for DamlContractId {
fn from(contract_id: &str) -> Self {
DamlContractId::new(contract_id)
}
}
impl From<String> for DamlContractId {
fn from(contract_id: String) -> Self {
DamlContractId::new(contract_id)
}
}
impl PartialEq<&DamlContractId> for &str {
fn eq(&self, other: &&DamlContractId) -> bool {
*self == other.contract_id
}
}
impl PartialEq<&str> for &DamlContractId {
fn eq(&self, other: &&str) -> bool {
self.contract_id == *other
}
}
impl PartialEq<DamlContractId> for &str {
fn eq(&self, other: &DamlContractId) -> bool {
self == &other.contract_id
}
}
impl PartialEq<&str> for DamlContractId {
fn eq(&self, other: &&str) -> bool {
&self.contract_id == other
}
}
impl std::fmt::Display for DamlContractId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.contract_id.fmt(f)
}
}
#[derive(Debug, Eq, Default, Clone)]
pub struct DamlTextMapImpl<T>(pub HashMap<DamlText, T>);
impl<T> DamlTextMapImpl<T> {
pub fn new() -> Self {
DamlTextMapImpl(HashMap::new())
}
}
impl<T: PartialEq> PartialOrd for DamlTextMapImpl<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.0.len() != other.0.len() {
return self.0.len().partial_cmp(&other.0.len());
}
self.0.keys().sorted().partial_cmp(other.0.keys().sorted())
}
}
impl<T: PartialOrd + Ord> Ord for DamlTextMapImpl<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).expect("PartialOrd is never None")
}
}
impl<T> PartialEq for DamlTextMapImpl<T> {
fn eq(&self, other: &Self) -> bool {
if self.0.len() != other.0.len() {
return false;
}
self.0.keys().all(|key| other.get(key).is_some())
}
}
impl<T> From<HashMap<DamlText, T>> for DamlTextMapImpl<T> {
fn from(m: HashMap<DamlText, T>) -> Self {
Self(m)
}
}
impl<T> Deref for DamlTextMapImpl<T> {
type Target = HashMap<DamlText, T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for DamlTextMapImpl<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<V> IntoIterator for DamlTextMapImpl<V> {
type IntoIter = <HashMap<DamlText, V> as IntoIterator>::IntoIter;
type Item = (DamlText, V);
fn into_iter(self) -> Self::IntoIter {
HashMap::into_iter(self.0)
}
}
impl<V> FromIterator<(DamlText, V)> for DamlTextMapImpl<V> {
fn from_iter<T: IntoIterator<Item = (DamlText, V)>>(iter: T) -> DamlTextMapImpl<V> {
Self::from(HashMap::from_iter(iter))
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)]
pub struct DamlFixedNumeric<T: Nat> {
pub _phantom: PhantomData<T>,
pub value: BigDecimal,
}
impl<T: Nat> DamlFixedNumeric<T> {
pub fn new(value: BigDecimal) -> Self {
Self {
_phantom: PhantomData::<T>::default(),
value,
}
}
pub fn try_new(f: f64) -> Result<Self, DamlError> {
Ok(Self::new(BigDecimal::try_from(f)?))
}
}
#[allow(clippy::fallible_impl_from)]
impl<T: Nat> From<f64> for DamlFixedNumeric<T> {
fn from(f: f64) -> Self {
Self::new(match BigDecimal::try_from(f) {
Ok(bd) => bd,
Err(err) => panic!("invalid f64: {}", err),
})
}
}
impl<T: Nat> FromStr for DamlFixedNumeric<T> {
type Err = DamlError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::new(FromStr::from_str(s)?))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_numeric_try_new() {
let num = DamlNumeric10::try_new(1.2_f64).unwrap();
assert_eq!(DamlNumeric10::new(BigDecimal::try_from(1.2_f64).unwrap()), num);
}
#[test]
fn test_numeric_try_new_nan() {
let num = DamlNumeric10::try_new(1_f64 / 0_f64);
assert!(num.is_err());
}
#[test]
fn test_numeric_from() {
let num = DamlNumeric10::from(1.2_f64);
assert_eq!(DamlNumeric10::new(BigDecimal::try_from(1.2_f64).unwrap()), num);
}
#[test]
#[should_panic]
fn test_numeric_from_should_panic() {
let _panic = DamlNumeric10::from(1_f64 / 0_f64);
}
#[test]
fn test_daml_text_map_equal_keys() {
let map1: DamlTextMapImpl<DamlInt64> =
vec![("key1".into(), 10), ("key2".into(), 20)].into_iter().collect::<DamlTextMap<DamlInt64>>();
let map2: DamlTextMap<DamlInt64> =
vec![("key1".into(), 100), ("key2".into(), 200)].into_iter().collect::<DamlTextMap<DamlInt64>>();
assert_eq!(map1, map2);
}
#[test]
fn test_daml_text_map_not_equal_keys() {
let map1: DamlTextMap<DamlInt64> =
vec![("key1".into(), 10), ("key2".into(), 20)].into_iter().collect::<DamlTextMap<DamlInt64>>();
let map2: DamlTextMap<DamlInt64> =
vec![("key3".into(), 10), ("key4".into(), 20)].into_iter().collect::<DamlTextMap<DamlInt64>>();
assert_ne!(map1, map2);
}
#[test]
fn test_daml_text_map_order() {
let map1: DamlTextMap<DamlInt64> =
vec![("key1".into(), 10), ("key2".into(), 20)].into_iter().collect::<DamlTextMap<DamlInt64>>();
let map2: DamlTextMap<DamlInt64> =
vec![("key2".into(), 10), ("key3".into(), 20)].into_iter().collect::<DamlTextMap<DamlInt64>>();
let map3: DamlTextMap<DamlInt64> =
vec![("key1".into(), 100), ("key2".into(), 200)].into_iter().collect::<DamlTextMap<DamlInt64>>();
assert_eq!(Ordering::Less, map1.cmp(&map2));
assert_eq!(Ordering::Greater, map2.cmp(&map1));
assert_eq!(Ordering::Equal, map1.cmp(&map3));
}
}