use std::convert::TryFrom;
use std::time::Duration;
use chrono::{DateTime, Utc};
use crate::data::command::DamlCommand;
use crate::data::completion::DamlCompletion;
use crate::data::identifier::DamlIdentifier;
use crate::data::value::DamlValue;
use crate::data::{DamlError, DamlResult};
use crate::grpc_protobuf::com::daml::ledger::api::v2::admin::{
CommandState, CommandStatus, CommandUpdates, Contract, RequestStatistics, Timing,
};
use crate::util;
use crate::util::Required;
#[derive(Debug, Eq, PartialEq, Clone, Copy, Default)]
pub enum DamlCommandState {
#[default]
Unspecified,
Pending,
Succeeded,
Failed,
}
impl From<CommandState> for DamlCommandState {
fn from(s: CommandState) -> Self {
match s {
CommandState::Unspecified => Self::Unspecified,
CommandState::Pending => Self::Pending,
CommandState::Succeeded => Self::Succeeded,
CommandState::Failed => Self::Failed,
}
}
}
impl From<DamlCommandState> for CommandState {
fn from(s: DamlCommandState) -> Self {
match s {
DamlCommandState::Unspecified => Self::Unspecified,
DamlCommandState::Pending => Self::Pending,
DamlCommandState::Succeeded => Self::Succeeded,
DamlCommandState::Failed => Self::Failed,
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Default)]
pub struct DamlTiming {
pub description: String,
pub duration: Duration,
}
impl From<Timing> for DamlTiming {
fn from(t: Timing) -> Self {
Self {
description: t.description,
duration: Duration::from_millis(u64::from(t.duration_ms)),
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy, Default)]
pub struct DamlRequestStatistics {
pub envelopes: u32,
pub request_size: u32,
pub recipients: u32,
}
impl From<RequestStatistics> for DamlRequestStatistics {
fn from(s: RequestStatistics) -> Self {
Self {
envelopes: s.envelopes,
request_size: s.request_size,
recipients: s.recipients,
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Default)]
pub struct DamlCommandUpdates {
pub created: Vec<DamlInspectedContract>,
pub archived: Vec<DamlInspectedContract>,
pub exercised: u32,
pub fetched: u32,
pub looked_up_by_key: u32,
}
impl TryFrom<CommandUpdates> for DamlCommandUpdates {
type Error = DamlError;
fn try_from(u: CommandUpdates) -> DamlResult<Self> {
Ok(Self {
created: u.created.into_iter().map(DamlInspectedContract::try_from).collect::<DamlResult<_>>()?,
archived: u.archived.into_iter().map(DamlInspectedContract::try_from).collect::<DamlResult<_>>()?,
exercised: u.exercised,
fetched: u.fetched,
looked_up_by_key: u.looked_up_by_key,
})
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct DamlInspectedContract {
pub template_id: DamlIdentifier,
pub contract_id: String,
pub contract_key: Option<DamlValue>,
}
impl TryFrom<Contract> for DamlInspectedContract {
type Error = DamlError;
fn try_from(c: Contract) -> DamlResult<Self> {
Ok(Self {
template_id: DamlIdentifier::from(c.template_id.req()?),
contract_id: c.contract_id,
contract_key: c.contract_key.map(DamlValue::try_from).transpose()?,
})
}
}
#[derive(Debug, Eq, PartialEq, Clone, Default)]
pub struct DamlCommandStatus {
pub started: DateTime<Utc>,
pub completed: Option<DateTime<Utc>>,
pub completion: Option<DamlCompletion>,
pub state: DamlCommandState,
pub commands: Vec<DamlCommand>,
pub request_statistics: Option<DamlRequestStatistics>,
pub updates: Option<DamlCommandUpdates>,
pub synchronizer_id: String,
pub timings: Vec<DamlTiming>,
}
impl TryFrom<CommandStatus> for DamlCommandStatus {
type Error = DamlError;
fn try_from(s: CommandStatus) -> DamlResult<Self> {
Ok(Self {
started: util::from_grpc_timestamp(&s.started.req()?)?,
completed: s.completed.as_ref().map(util::from_grpc_timestamp).transpose()?,
completion: s.completion.map(DamlCompletion::try_from).transpose()?,
state: DamlCommandState::from(CommandState::try_from(s.state).ok().req()?),
commands: s.commands.into_iter().map(DamlCommand::try_from).collect::<DamlResult<_>>()?,
request_statistics: s.request_statistics.map(DamlRequestStatistics::from),
updates: s.updates.map(DamlCommandUpdates::try_from).transpose()?,
synchronizer_id: s.synchronizer_id,
timings: s.timings.into_iter().map(DamlTiming::from).collect(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_command_state_round_trips() {
let cases = [
DamlCommandState::Unspecified,
DamlCommandState::Pending,
DamlCommandState::Succeeded,
DamlCommandState::Failed,
];
for original in cases {
let proto: CommandState = original.into();
let back = DamlCommandState::from(proto);
assert_eq!(back, original);
}
}
#[test]
fn timing_preserves_duration_in_ms() {
let proto = Timing {
description: "validation".to_owned(),
duration_ms: 1_500,
};
let dto: DamlTiming = proto.into();
assert_eq!(dto.description, "validation");
assert_eq!(dto.duration, Duration::from_millis(1_500));
}
#[test]
fn request_statistics_preserves_counts() {
let proto = RequestStatistics {
envelopes: 7,
request_size: 1024,
recipients: 3,
};
let dto: DamlRequestStatistics = proto.into();
assert_eq!(
dto,
DamlRequestStatistics {
envelopes: 7,
request_size: 1024,
recipients: 3,
},
);
}
}