use std::collections::BTreeMap;
use std::fmt;
use std::marker::PhantomData;
#[derive(
Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
#[serde(transparent)]
pub struct Party(pub(crate) String);
const PARTY_MAX_LEN: usize = 255;
const fn is_party_char(c: char) -> bool {
c.is_ascii_alphanumeric() || matches!(c, ':' | '-' | '_' | ' ')
}
impl Party {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn parse(id: impl Into<String>) -> Result<Self, PartyParseError> {
let id = id.into();
if id.is_empty() {
return Err(PartyParseError::Empty);
}
if id.chars().count() > PARTY_MAX_LEN {
return Err(PartyParseError::TooLong {
length: id.chars().count(),
});
}
if let Some(c) = id.chars().find(|c| !is_party_char(*c)) {
return Err(PartyParseError::UnexpectedCharacter { character: c });
}
Ok(Self(id))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn into_string(self) -> String {
self.0
}
}
impl fmt::Display for Party {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl AsRef<str> for Party {
fn as_ref(&self) -> &str {
&self.0
}
}
impl From<String> for Party {
fn from(id: String) -> Self {
Self(id)
}
}
impl From<&str> for Party {
fn from(id: &str) -> Self {
Self::new(id)
}
}
impl From<Party> for String {
fn from(party: Party) -> Self {
party.0
}
}
pub struct ContractId<T> {
id: String,
_marker: PhantomData<fn() -> T>,
}
impl<T> ContractId<T> {
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
_marker: PhantomData,
}
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.id
}
#[must_use]
pub fn retag<U>(self) -> ContractId<U> {
ContractId::new(self.id)
}
}
impl<T> Clone for ContractId<T> {
fn clone(&self) -> Self {
Self::new(self.id.clone())
}
}
impl<T> fmt::Debug for ContractId<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("ContractId").field(&self.id).finish()
}
}
impl<T> PartialEq for ContractId<T> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<T> Eq for ContractId<T> {}
impl<T> std::hash::Hash for ContractId<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl<T> PartialOrd for ContractId<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for ContractId<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.id.cmp(&other.id)
}
}
#[derive(Clone, Debug)]
pub struct Numeric(pub(crate) String);
impl Numeric {
pub fn parse(text: impl Into<String>) -> Result<Self, NumericParseError> {
let text = text.into();
if canonical_decimal(&text).is_some() {
Ok(Self(text))
} else {
Err(NumericParseError { input: text })
}
}
#[must_use]
pub fn from_wire(text: impl Into<String>) -> Self {
Self(text.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
fn canonical(&self) -> Option<String> {
canonical_decimal(&self.0)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum PartyParseError {
Empty,
TooLong {
length: usize,
},
UnexpectedCharacter {
character: char,
},
}
impl fmt::Display for PartyParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => f.write_str("a party id cannot be empty"),
Self::TooLong { length } => write!(
f,
"a party id cannot exceed {PARTY_MAX_LEN} characters (got {length})"
),
Self::UnexpectedCharacter { character } => write!(
f,
"`{character}` is not valid in a party id \
(expected letters, digits, `:`, `-`, `_`, space)"
),
}
}
}
impl std::error::Error for PartyParseError {}
impl std::str::FromStr for Party {
type Err = PartyParseError;
fn from_str(id: &str) -> Result<Self, Self::Err> {
Self::parse(id)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NumericParseError {
input: String,
}
impl NumericParseError {
#[must_use]
pub fn input(&self) -> &str {
&self.input
}
}
impl fmt::Display for NumericParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"`{}` is not a decimal literal (expected -?digits[.digits])",
self.input
)
}
}
impl std::error::Error for NumericParseError {}
impl std::str::FromStr for Numeric {
type Err = NumericParseError;
fn from_str(text: &str) -> Result<Self, Self::Err> {
Self::parse(text)
}
}
impl fmt::Display for Numeric {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl TryFrom<&str> for Numeric {
type Error = NumericParseError;
fn try_from(text: &str) -> Result<Self, Self::Error> {
Self::parse(text)
}
}
fn canonical_decimal(raw: &str) -> Option<String> {
let (negative, digits) = match raw.strip_prefix('-') {
Some(rest) => (true, rest),
None => (false, raw.strip_prefix('+').unwrap_or(raw)),
};
let (int, frac) = digits.split_once('.').unwrap_or((digits, ""));
let valid = !int.is_empty()
&& int.bytes().all(|b| b.is_ascii_digit())
&& frac.bytes().all(|b| b.is_ascii_digit());
if !valid {
return None;
}
let int = int.trim_start_matches('0');
let int = if int.is_empty() { "0" } else { int };
let frac = frac.trim_end_matches('0');
let mut canonical = String::with_capacity(raw.len() + 1);
if negative && !(int == "0" && frac.is_empty()) {
canonical.push('-');
}
canonical.push_str(int);
if !frac.is_empty() {
canonical.push('.');
canonical.push_str(frac);
}
Some(canonical)
}
impl PartialEq for Numeric {
fn eq(&self, other: &Self) -> bool {
match (self.canonical(), other.canonical()) {
(Some(a), Some(b)) => a == b,
(None, None) => self.0 == other.0,
_ => false,
}
}
}
impl Eq for Numeric {}
impl std::hash::Hash for Numeric {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self.canonical() {
Some(canonical) => canonical.hash(state),
None => self.0.hash(state),
}
}
}
impl PartialOrd for Numeric {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Numeric {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use std::cmp::Ordering;
let (Some(a), Some(b)) = (self.canonical(), other.canonical()) else {
return match (self.canonical(), other.canonical()) {
(Some(_), None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater,
_ => self.0.cmp(&other.0),
};
};
let (neg_a, mag_a) = a
.strip_prefix('-')
.map_or((false, a.as_str()), |m| (true, m));
let (neg_b, mag_b) = b
.strip_prefix('-')
.map_or((false, b.as_str()), |m| (true, m));
match (neg_a, neg_b) {
(false, true) => Ordering::Greater,
(true, false) => Ordering::Less,
(negative, _) => {
let ordering = magnitude_cmp(mag_a, mag_b);
if negative {
ordering.reverse()
} else {
ordering
}
}
}
}
}
fn magnitude_cmp(a: &str, b: &str) -> std::cmp::Ordering {
let (int_a, frac_a) = a.split_once('.').unwrap_or((a, ""));
let (int_b, frac_b) = b.split_once('.').unwrap_or((b, ""));
int_a
.len()
.cmp(&int_b.len())
.then_with(|| int_a.cmp(int_b))
.then_with(|| frac_a.cmp(frac_b))
}
impl serde::Serialize for Numeric {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.0)
}
}
impl<'de> serde::Deserialize<'de> for Numeric {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct NumericVisitor;
impl serde::de::Visitor<'_> for NumericVisitor {
type Value = Numeric;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a Daml Numeric as a decimal string or a JSON number")
}
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Numeric, E> {
Ok(Numeric(value.to_string()))
}
fn visit_i64<E: serde::de::Error>(self, value: i64) -> Result<Numeric, E> {
Ok(Numeric(value.to_string()))
}
fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<Numeric, E> {
Ok(Numeric(value.to_string()))
}
fn visit_f64<E: serde::de::Error>(self, value: f64) -> Result<Numeric, E> {
Ok(Numeric(value.to_string()))
}
}
deserializer.deserialize_any(NumericVisitor)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Int64(pub i64);
impl From<i64> for Int64 {
fn from(value: i64) -> Self {
Self(value)
}
}
impl From<Int64> for i64 {
fn from(value: Int64) -> Self {
value.0
}
}
impl fmt::Display for Int64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::str::FromStr for Int64 {
type Err = std::num::ParseIntError;
fn from_str(text: &str) -> Result<Self, Self::Err> {
text.parse().map(Self)
}
}
impl serde::Serialize for Int64 {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.0.to_string())
}
}
impl<'de> serde::Deserialize<'de> for Int64 {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct Int64Visitor;
impl serde::de::Visitor<'_> for Int64Visitor {
type Value = Int64;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("an Int64 as a JSON number or a string")
}
fn visit_i64<E: serde::de::Error>(self, value: i64) -> Result<Int64, E> {
Ok(Int64(value))
}
fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<Int64, E> {
i64::try_from(value)
.map(Int64)
.map_err(serde::de::Error::custom)
}
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Int64, E> {
value
.parse::<i64>()
.map(Int64)
.map_err(serde::de::Error::custom)
}
}
deserializer.deserialize_any(Int64Visitor)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timestamp(pub i64);
impl Timestamp {
#[must_use]
pub fn micros(self) -> i64 {
self.0
}
#[must_use]
pub fn to_datetime(self) -> Option<time::OffsetDateTime> {
time::OffsetDateTime::from_unix_timestamp_nanos(i128::from(self.0) * 1_000).ok()
}
#[must_use]
pub fn from_datetime(at: time::OffsetDateTime) -> Option<Self> {
let micros = at.unix_timestamp_nanos().div_euclid(1_000);
i64::try_from(micros).ok().map(Self)
}
}
impl From<Timestamp> for i64 {
fn from(at: Timestamp) -> Self {
at.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Date(pub i32);
impl Date {
#[must_use]
pub fn days(self) -> i32 {
self.0
}
#[must_use]
pub fn to_date(self) -> Option<time::Date> {
UNIX_EPOCH_DATE.checked_add(time::Duration::days(i64::from(self.0)))
}
#[must_use]
pub fn from_date(date: time::Date) -> Option<Self> {
i32::try_from((date - UNIX_EPOCH_DATE).whole_days())
.ok()
.map(Self)
}
}
impl From<Date> for i32 {
fn from(date: Date) -> Self {
date.0
}
}
pub type TextMap<V> = BTreeMap<String, V>;
#[derive(Clone, Debug, Eq, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct GenMap<K, V>(pub(crate) Vec<(K, V)>);
impl<K, V> Default for GenMap<K, V> {
fn default() -> Self {
Self(Vec::new())
}
}
impl<K, V> GenMap<K, V> {
#[must_use]
pub fn new() -> Self {
Self(Vec::new())
}
pub fn iter(&self) -> std::slice::Iter<'_, (K, V)> {
self.0.iter()
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn insert(&mut self, key: K, value: V) {
self.0.push((key, value));
}
}
impl<K, V> From<Vec<(K, V)>> for GenMap<K, V> {
fn from(entries: Vec<(K, V)>) -> Self {
Self(entries)
}
}
impl<K, V> FromIterator<(K, V)> for GenMap<K, V> {
fn from_iter<I: IntoIterator<Item = (K, V)>>(entries: I) -> Self {
Self(entries.into_iter().collect())
}
}
impl<K, V> IntoIterator for GenMap<K, V> {
type Item = (K, V);
type IntoIter = std::vec::IntoIter<(K, V)>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a, K, V> IntoIterator for &'a GenMap<K, V> {
type Item = &'a (K, V);
type IntoIter = std::slice::Iter<'a, (K, V)>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<K: PartialEq, V: PartialEq> PartialEq for GenMap<K, V> {
fn eq(&self, other: &Self) -> bool {
if self.0.len() != other.0.len() {
return false;
}
let mut matched = vec![false; other.0.len()];
self.0.iter().all(|entry| {
other
.0
.iter()
.enumerate()
.position(|(index, candidate)| !matched[index] && candidate == entry)
.is_some_and(|index| {
matched[index] = true;
true
})
})
}
}
const UNIX_EPOCH_DATE: time::Date = time::macros::date!(1970 - 01 - 01);
const DATE_FORMAT: &[time::format_description::FormatItem<'static>] =
time::macros::format_description!("[year]-[month]-[day]");
impl<T> serde::Serialize for ContractId<T> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.id)
}
}
impl<'de, T> serde::Deserialize<'de> for ContractId<T> {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self::new(String::deserialize(deserializer)?))
}
}
impl serde::Serialize for Timestamp {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let datetime = time::OffsetDateTime::from_unix_timestamp_nanos(i128::from(self.0) * 1_000)
.map_err(serde::ser::Error::custom)?;
let text = datetime
.format(&time::format_description::well_known::Rfc3339)
.map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&text)
}
}
impl<'de> serde::Deserialize<'de> for Timestamp {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let text = String::deserialize(deserializer)?;
let datetime =
time::OffsetDateTime::parse(&text, &time::format_description::well_known::Rfc3339)
.map_err(serde::de::Error::custom)?;
let micros = datetime.unix_timestamp_nanos().div_euclid(1_000);
Ok(Self(
i64::try_from(micros).map_err(serde::de::Error::custom)?,
))
}
}
impl serde::Serialize for Date {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let date = UNIX_EPOCH_DATE
.checked_add(time::Duration::days(i64::from(self.0)))
.ok_or_else(|| serde::ser::Error::custom("date out of range"))?;
let text = date
.format(DATE_FORMAT)
.map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&text)
}
}
impl<'de> serde::Deserialize<'de> for Date {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let text = String::deserialize(deserializer)?;
let date = time::Date::parse(&text, DATE_FORMAT).map_err(serde::de::Error::custom)?;
let days = (date - UNIX_EPOCH_DATE).whole_days();
Ok(Self(i32::try_from(days).map_err(serde::de::Error::custom)?))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct NestedOpt<T>(pub Option<T>);
impl<T> NestedOpt<T> {
pub fn new(value: Option<T>) -> Self {
Self(value)
}
#[must_use]
pub fn into_inner(self) -> Option<T> {
self.0
}
}
impl<T> From<Option<T>> for NestedOpt<T> {
fn from(value: Option<T>) -> Self {
Self(value)
}
}
impl<T> From<NestedOpt<T>> for Option<T> {
fn from(value: NestedOpt<T>) -> Self {
value.0
}
}
impl<T: serde::Serialize> serde::Serialize for NestedOpt<T> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match &self.0 {
None => serializer.collect_seq(core::iter::empty::<&T>()),
Some(value) => serializer.collect_seq(core::iter::once(value)),
}
}
}
impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for NestedOpt<T> {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let mut items = Vec::<T>::deserialize(deserializer)?;
match items.len() {
0 => Ok(Self(None)),
1 => Ok(Self(Some(items.remove(0)))),
n => Err(serde::de::Error::custom(format!(
"nested Optional expects a 0- or 1-element array, got {n}"
))),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Unit;
impl serde::Serialize for Unit {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
serializer.serialize_map(Some(0))?.end()
}
}
impl<'de> serde::Deserialize<'de> for Unit {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct UnitVisitor;
impl<'de> serde::de::Visitor<'de> for UnitVisitor {
type Value = Unit;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("the empty object `{}` (Daml Unit)")
}
fn visit_map<A: serde::de::MapAccess<'de>>(self, mut map: A) -> Result<Unit, A::Error> {
while map
.next_entry::<serde::de::IgnoredAny, serde::de::IgnoredAny>()?
.is_some()
{}
Ok(Unit)
}
fn visit_unit<E: serde::de::Error>(self) -> Result<Unit, E> {
Ok(Unit)
}
}
deserializer.deserialize_any(UnitVisitor)
}
}