use futures_core::future::BoxFuture;
use time::Duration;
use crate::execute::execute;
use crate::query::cost::QueryCost;
use crate::query::payment_transaction::PaymentTransaction;
use crate::{
AccountId,
Client,
Error,
Hbar,
TransactionId,
TransactionReceiptQuery,
};
mod any;
mod cost;
mod execute;
pub(super) mod payment_transaction;
mod protobuf;
pub(crate) use any::AnyQueryData;
pub use any::{
AnyQuery,
AnyQueryResponse,
};
pub(crate) use execute::{
response_header,
QueryExecute,
};
pub(crate) use protobuf::ToQueryProtobuf;
#[derive(Debug, Default)]
pub struct Query<D>
where
D: QueryExecute,
{
pub(crate) data: D,
pub(crate) payment: PaymentTransaction,
}
impl<D> Query<D>
where
D: QueryExecute + Default,
{
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl<D> Query<D>
where
D: QueryExecute,
{
#[must_use]
pub fn get_node_account_ids(&self) -> Option<&[AccountId]> {
self.payment.get_node_account_ids()
}
pub fn node_account_ids(&mut self, ids: impl IntoIterator<Item = AccountId>) -> &mut Self {
self.payment.node_account_ids(ids);
self
}
#[must_use]
pub fn get_payment_amount(&self) -> Option<Hbar> {
self.payment.get_amount()
}
pub fn payment_amount(&mut self, amount: Hbar) -> &mut Self {
self.payment.amount(amount);
self
}
#[must_use]
pub fn get_max_amount(&self) -> Option<Hbar> {
self.payment.get_max_amount()
}
pub fn max_payment_amount(&mut self, max: impl Into<Option<Hbar>>) -> &mut Self {
self.payment.max_amount(max);
self
}
#[must_use]
pub fn get_payment_transaction_valid_duration(&self) -> Option<Duration> {
self.payment.get_transaction_valid_duration()
}
pub fn payment_transaction_valid_duration(&mut self, duration: Duration) -> &mut Self {
self.payment.transaction_valid_duration(duration);
self
}
#[must_use]
pub fn get_max_payment_transaction_fee(&self) -> Option<Hbar> {
self.payment.get_max_transaction_fee()
}
pub fn max_payment_transaction_fee(&mut self, fee: Hbar) -> &mut Self {
self.payment.max_transaction_fee(fee);
self
}
#[must_use]
pub fn get_payment_transaction_memo(&self) -> &str {
self.payment.get_transaction_memo()
}
pub fn payment_transaction_memo(&mut self, memo: impl AsRef<str>) -> &mut Self {
self.payment.transaction_memo(memo);
self
}
#[must_use]
pub fn get_payment_transaction_id(&self) -> Option<TransactionId> {
self.payment.get_transaction_id()
}
pub fn payment_transaction_id(&mut self, id: TransactionId) -> &mut Self {
self.payment.transaction_id(id);
self
}
pub async fn get_cost(&self, client: &Client) -> crate::Result<Hbar> {
self.get_cost_with_optional_timeout(client, None).await
}
pub(crate) async fn get_cost_with_optional_timeout(
&self,
client: &Client,
timeout: Option<std::time::Duration>,
) -> crate::Result<Hbar> {
if !self.data.is_payment_required() {
return Ok(Hbar::ZERO);
}
QueryCost::new(self).execute(client, timeout).await
}
pub async fn get_cost_with_timeout(
&self,
client: &Client,
timeout: std::time::Duration,
) -> crate::Result<Hbar> {
self.get_cost_with_optional_timeout(client, Some(timeout)).await
}
}
impl<D> Query<D>
where
D: QueryExecute,
{
#[allow(clippy::missing_errors_doc)]
pub async fn execute(&mut self, client: &Client) -> crate::Result<D::Response> {
self.execute_with_optional_timeout(client, None).await
}
pub(crate) async fn execute_with_optional_timeout(
&mut self,
client: &Client,
timeout: Option<std::time::Duration>,
) -> crate::Result<D::Response> {
fn recurse_receipt(
transaction_id: &TransactionId,
client: Client,
timeout: Option<std::time::Duration>,
) -> BoxFuture<'_, ()> {
Box::pin(async move {
let _ = TransactionReceiptQuery::new()
.transaction_id(*transaction_id)
.execute_with_optional_timeout(&client, timeout)
.await;
})
}
if let Some(transaction_id) = self.data.transaction_id() {
if self.data.is_payment_required() {
let client = client.clone();
recurse_receipt(&transaction_id, client, timeout).await;
}
}
if self.payment.get_amount().is_none() && self.data.is_payment_required() {
let cost = QueryCost::new(self).execute(client, None).await?;
if self.payment.get_max_amount().is_none() {
self.payment.max_amount(client.default_max_query_payment());
}
if let Some(max_amount) = self.payment.get_max_amount() {
if cost > max_amount {
return Err(Error::MaxQueryPaymentExceeded {
query_cost: cost,
max_query_payment: max_amount,
});
}
}
self.payment.amount(cost);
}
if self.data.is_payment_required() {
self.payment.freeze_with(client)?;
}
execute(client, self, timeout).await
}
#[allow(clippy::missing_errors_doc)]
pub async fn execute_with_timeout(
&mut self,
client: &Client,
timeout: std::time::Duration,
) -> crate::Result<D::Response> {
self.execute_with_optional_timeout(client, Some(timeout)).await
}
}