use std::time::Duration;
use canton_proto::com::daml::ledger::api::v2 as pb;
use crate::client::CantonClient;
use crate::command::ChangeId;
use crate::request::TransactionShape;
use canton_core::Result;
#[derive(Clone, Debug)]
pub struct Submission {
client: CantonClient,
change_id: ChangeId,
commands: pb::Commands,
shape: TransactionShape,
}
impl Submission {
pub(crate) fn new(
client: CantonClient,
change_id: ChangeId,
commands: pb::Commands,
shape: TransactionShape,
) -> Self {
Self {
client,
change_id,
commands,
shape,
}
}
#[must_use]
pub fn change_id(&self) -> &ChangeId {
&self.change_id
}
pub async fn submit(&self) -> Result<()> {
self.client.submit_commands(self.commands.clone()).await
}
pub async fn submit_and_wait(&self) -> Result<pb::SubmitAndWaitResponse> {
self.client
.submit_and_wait_commands(self.commands.clone())
.await
}
pub async fn submit_and_wait_for_transaction(&self) -> Result<pb::Transaction> {
self.client
.submit_and_wait_for_transaction_commands(self.commands.clone(), self.shape)
.await
}
pub async fn recover(&self, begin_offset: i64, timeout: Duration) -> Result<pb::Completion> {
self.client
.await_completion(&self.change_id, begin_offset, timeout)
.await
}
}
#[derive(Clone, Debug)]
pub struct JsonSubmission {
client: crate::JsonClient,
commands: crate::JsonCommands,
change_id: ChangeId,
}
impl JsonSubmission {
pub(crate) fn new(client: crate::JsonClient, commands: crate::JsonCommands) -> Self {
let change_id = commands.change_id();
Self {
client,
commands,
change_id,
}
}
#[must_use]
pub fn change_id(&self) -> &ChangeId {
&self.change_id
}
pub async fn submit(&self) -> Result<()> {
self.client.submit(&self.commands).await
}
pub async fn submit_and_wait(&self) -> Result<crate::JsonSubmitAndWaitResponse> {
self.client.submit_and_wait(&self.commands).await
}
pub async fn submit_and_wait_for_transaction(&self) -> Result<crate::JsonSubmitResponse> {
self.client
.submit_and_wait_for_transaction(&self.commands)
.await
}
#[cfg(feature = "ws")]
#[cfg_attr(docsrs, doc(cfg(feature = "ws")))]
#[allow(clippy::large_futures)] pub async fn recover(&self, begin_offset: i64, timeout: Duration) -> Result<serde_json::Value> {
use canton_core::Error;
use tokio_stream::StreamExt as _;
let scan = Box::pin(async {
let stream = self
.client
.ws_completions(self.change_id.act_as().to_vec(), begin_offset)
.await?;
tokio::pin!(stream);
while let Some(item) = stream.next().await {
let frame = item?;
let Some(completion) = crate::ws::completion_value(&frame) else {
continue;
};
if !self.change_id.matches_json(completion) {
continue;
}
if let Some(status) = completion.get("status") {
let code = status.get("code").and_then(serde_json::Value::as_i64);
if code.is_some_and(|code| code != 0) {
return Err(Error::CommandRejected {
code: code.unwrap_or_default().to_string(),
message: status
.get("message")
.and_then(serde_json::Value::as_str)
.unwrap_or_default()
.to_string(),
});
}
}
return Ok(completion.clone());
}
Err(Error::UnexpectedResponse(format!(
"completion stream ended before command {} was seen",
self.change_id.command_id()
)))
});
Box::pin(tokio::time::timeout(timeout, scan))
.await
.map_err(|_| canton_core::Error::Timeout)?
}
}