daml_grpc/
primitive_types.rs

1use crate::data::DamlError;
2use crate::nat::{Nat, Nat10};
3use bigdecimal::BigDecimal;
4use chrono::{Date, DateTime, Utc};
5use itertools::Itertools;
6use std::cmp::Ordering;
7use std::collections::{BTreeMap, HashMap};
8use std::convert::TryFrom;
9use std::fmt::Formatter;
10use std::iter::FromIterator;
11use std::marker::PhantomData;
12use std::ops::{Deref, DerefMut};
13use std::str::FromStr;
14
15/// Type alias for a Daml `Int`.
16pub type DamlInt64 = i64;
17
18/// Type alias for a Daml `Numeric`.
19pub type DamlNumeric = BigDecimal;
20
21/// Type alias for a Daml `Numeric 10`
22pub type DamlNumeric10 = DamlFixedNumeric<Nat10>;
23
24/// Type alias for a Daml `Text`.
25pub type DamlText = String;
26
27/// Type alias for a Daml `Timestamp`.
28pub type DamlTimestamp = DateTime<Utc>;
29
30/// Type alias for a Daml `Bool`.
31pub type DamlBool = bool;
32
33/// Type alias for a Daml `Unit`.
34pub type DamlUnit = ();
35
36/// Type alias for a Daml `Date`.
37pub type DamlDate = Date<Utc>;
38
39/// Type alias for a Daml `List a`.
40pub type DamlList<T> = Vec<T>;
41
42/// Type alias for a Daml legacy `TextMap a b`.
43pub type DamlTextMap<V> = DamlTextMapImpl<V>;
44
45/// Type alias for a Daml `GenMap a b`.
46pub type DamlGenMap<K, V> = BTreeMap<K, V>;
47
48/// Type alias for a Daml `Optional a`.
49pub type DamlOptional<T> = Option<T>;
50
51/// A Daml `Party`.
52#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone)]
53pub struct DamlParty {
54    pub party: String,
55}
56
57impl DamlParty {
58    pub fn new(party: impl Into<String>) -> Self {
59        Self {
60            party: party.into(),
61        }
62    }
63
64    pub fn as_str(&self) -> &str {
65        self.party.as_str()
66    }
67}
68
69impl From<&str> for DamlParty {
70    fn from(party: &str) -> Self {
71        DamlParty::new(party)
72    }
73}
74
75impl From<String> for DamlParty {
76    fn from(party: String) -> Self {
77        DamlParty::new(party)
78    }
79}
80
81impl PartialEq<&DamlParty> for &str {
82    fn eq(&self, other: &&DamlParty) -> bool {
83        *self == other.party
84    }
85}
86
87impl PartialEq<&str> for &DamlParty {
88    fn eq(&self, other: &&str) -> bool {
89        self.party == *other
90    }
91}
92
93impl PartialEq<DamlParty> for &str {
94    fn eq(&self, other: &DamlParty) -> bool {
95        self == &other.party
96    }
97}
98
99impl PartialEq<&str> for DamlParty {
100    fn eq(&self, other: &&str) -> bool {
101        &self.party == other
102    }
103}
104
105impl std::fmt::Display for DamlParty {
106    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
107        self.party.fmt(f)
108    }
109}
110
111/// A Daml `ContractId`.
112#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone)]
113pub struct DamlContractId {
114    pub contract_id: String,
115}
116
117impl DamlContractId {
118    pub fn new(contract_id: impl Into<String>) -> Self {
119        Self {
120            contract_id: contract_id.into(),
121        }
122    }
123
124    pub fn as_str(&self) -> &str {
125        self.contract_id.as_str()
126    }
127}
128
129impl From<&str> for DamlContractId {
130    fn from(contract_id: &str) -> Self {
131        DamlContractId::new(contract_id)
132    }
133}
134
135impl From<String> for DamlContractId {
136    fn from(contract_id: String) -> Self {
137        DamlContractId::new(contract_id)
138    }
139}
140
141impl PartialEq<&DamlContractId> for &str {
142    fn eq(&self, other: &&DamlContractId) -> bool {
143        *self == other.contract_id
144    }
145}
146
147impl PartialEq<&str> for &DamlContractId {
148    fn eq(&self, other: &&str) -> bool {
149        self.contract_id == *other
150    }
151}
152
153impl PartialEq<DamlContractId> for &str {
154    fn eq(&self, other: &DamlContractId) -> bool {
155        self == &other.contract_id
156    }
157}
158
159impl PartialEq<&str> for DamlContractId {
160    fn eq(&self, other: &&str) -> bool {
161        &self.contract_id == other
162    }
163}
164
165impl std::fmt::Display for DamlContractId {
166    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
167        self.contract_id.fmt(f)
168    }
169}
170
171/// A Daml legacy `TextMap a`.
172#[derive(Debug, Eq, Default, Clone)]
173pub struct DamlTextMapImpl<T>(pub HashMap<DamlText, T>);
174
175impl<T> DamlTextMapImpl<T> {
176    pub fn new() -> Self {
177        DamlTextMapImpl(HashMap::new())
178    }
179}
180
181/// Determine the order of `DamlTextMapImpl` objects.
182///
183/// The ordering of `DamlTextMapImpl` objects is determined by the number of entries and then by the ordering of keys,
184/// values are not considered.
185impl<T: PartialEq> PartialOrd for DamlTextMapImpl<T> {
186    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
187        if self.0.len() != other.0.len() {
188            return self.0.len().partial_cmp(&other.0.len());
189        }
190        self.0.keys().sorted().partial_cmp(other.0.keys().sorted())
191    }
192}
193
194impl<T: PartialOrd + Ord> Ord for DamlTextMapImpl<T> {
195    fn cmp(&self, other: &Self) -> Ordering {
196        self.partial_cmp(other).expect("PartialOrd is never None")
197    }
198}
199
200/// Compare `DamlTextMapImpl` for equality.
201///
202/// `DamlTextMapImpl` objects are considered equal if they contain the same number of entries and the same keys, values
203/// are not considered.
204impl<T> PartialEq for DamlTextMapImpl<T> {
205    fn eq(&self, other: &Self) -> bool {
206        if self.0.len() != other.0.len() {
207            return false;
208        }
209        self.0.keys().all(|key| other.get(key).is_some())
210    }
211}
212
213impl<T> From<HashMap<DamlText, T>> for DamlTextMapImpl<T> {
214    fn from(m: HashMap<DamlText, T>) -> Self {
215        Self(m)
216    }
217}
218
219impl<T> Deref for DamlTextMapImpl<T> {
220    type Target = HashMap<DamlText, T>;
221
222    fn deref(&self) -> &Self::Target {
223        &self.0
224    }
225}
226
227impl<T> DerefMut for DamlTextMapImpl<T> {
228    fn deref_mut(&mut self) -> &mut Self::Target {
229        &mut self.0
230    }
231}
232
233impl<V> IntoIterator for DamlTextMapImpl<V> {
234    type IntoIter = <HashMap<DamlText, V> as IntoIterator>::IntoIter;
235    type Item = (DamlText, V);
236
237    fn into_iter(self) -> Self::IntoIter {
238        HashMap::into_iter(self.0)
239    }
240}
241
242impl<V> FromIterator<(DamlText, V)> for DamlTextMapImpl<V> {
243    fn from_iter<T: IntoIterator<Item = (DamlText, V)>>(iter: T) -> DamlTextMapImpl<V> {
244        Self::from(HashMap::from_iter(iter))
245    }
246}
247
248/// A fixed precision numeric type.  Currently a simple wrapper around a `BigDecimal`.
249#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)]
250pub struct DamlFixedNumeric<T: Nat> {
251    pub _phantom: PhantomData<T>,
252    pub value: BigDecimal,
253}
254
255impl<T: Nat> DamlFixedNumeric<T> {
256    pub fn new(value: BigDecimal) -> Self {
257        Self {
258            _phantom: PhantomData::<T>::default(),
259            value,
260        }
261    }
262
263    pub fn try_new(f: f64) -> Result<Self, DamlError> {
264        Ok(Self::new(BigDecimal::try_from(f)?))
265    }
266}
267
268/// Convert a f64 to a `DamlFixedNumeric`.
269///
270/// Note that this is not a fallible conversion and instead panics if the conversion fails.  Use
271/// `DamlFixedNumeric::try_new` instead to construct a `DamlFixedNumerical` with returns an error on invalid input.
272///
273/// Arguable we could use the `TryFrom` trait here however the code generate currently produces entries such as
274/// `my_numeric: impl Into<DamlNumeric10>` rather than `TryInto` which has the nice property of avoiding fallible cases.
275#[allow(clippy::fallible_impl_from)]
276impl<T: Nat> From<f64> for DamlFixedNumeric<T> {
277    fn from(f: f64) -> Self {
278        Self::new(match BigDecimal::try_from(f) {
279            Ok(bd) => bd,
280            Err(err) => panic!("invalid f64: {}", err),
281        })
282    }
283}
284
285impl<T: Nat> FromStr for DamlFixedNumeric<T> {
286    type Err = DamlError;
287
288    fn from_str(s: &str) -> Result<Self, Self::Err> {
289        Ok(Self::new(FromStr::from_str(s)?))
290    }
291}
292
293#[cfg(test)]
294mod tests {
295    use super::*;
296
297    #[test]
298    fn test_numeric_try_new() {
299        let num = DamlNumeric10::try_new(1.2_f64).unwrap();
300        assert_eq!(DamlNumeric10::new(BigDecimal::try_from(1.2_f64).unwrap()), num);
301    }
302
303    #[test]
304    fn test_numeric_try_new_nan() {
305        let num = DamlNumeric10::try_new(1_f64 / 0_f64);
306        assert!(num.is_err());
307    }
308
309    #[test]
310    fn test_numeric_from() {
311        let num = DamlNumeric10::from(1.2_f64);
312        assert_eq!(DamlNumeric10::new(BigDecimal::try_from(1.2_f64).unwrap()), num);
313    }
314
315    #[test]
316    #[should_panic]
317    fn test_numeric_from_should_panic() {
318        let _panic = DamlNumeric10::from(1_f64 / 0_f64);
319    }
320
321    #[test]
322    fn test_daml_text_map_equal_keys() {
323        let map1: DamlTextMapImpl<DamlInt64> =
324            vec![("key1".into(), 10), ("key2".into(), 20)].into_iter().collect::<DamlTextMap<DamlInt64>>();
325        let map2: DamlTextMap<DamlInt64> =
326            vec![("key1".into(), 100), ("key2".into(), 200)].into_iter().collect::<DamlTextMap<DamlInt64>>();
327        assert_eq!(map1, map2);
328    }
329
330    #[test]
331    fn test_daml_text_map_not_equal_keys() {
332        let map1: DamlTextMap<DamlInt64> =
333            vec![("key1".into(), 10), ("key2".into(), 20)].into_iter().collect::<DamlTextMap<DamlInt64>>();
334        let map2: DamlTextMap<DamlInt64> =
335            vec![("key3".into(), 10), ("key4".into(), 20)].into_iter().collect::<DamlTextMap<DamlInt64>>();
336        assert_ne!(map1, map2);
337    }
338
339    #[test]
340    fn test_daml_text_map_order() {
341        let map1: DamlTextMap<DamlInt64> =
342            vec![("key1".into(), 10), ("key2".into(), 20)].into_iter().collect::<DamlTextMap<DamlInt64>>();
343        let map2: DamlTextMap<DamlInt64> =
344            vec![("key2".into(), 10), ("key3".into(), 20)].into_iter().collect::<DamlTextMap<DamlInt64>>();
345        let map3: DamlTextMap<DamlInt64> =
346            vec![("key1".into(), 100), ("key2".into(), 200)].into_iter().collect::<DamlTextMap<DamlInt64>>();
347        assert_eq!(Ordering::Less, map1.cmp(&map2));
348        assert_eq!(Ordering::Greater, map2.cmp(&map1));
349        assert_eq!(Ordering::Equal, map1.cmp(&map3));
350    }
351}