1use futures_core::future::BoxFuture;
4use time::Duration;
5
6use crate::execute::execute;
7use crate::query::cost::QueryCost;
8use crate::query::payment_transaction::PaymentTransaction;
9use crate::{
10 AccountId,
11 Client,
12 Error,
13 Hbar,
14 TransactionId,
15 TransactionReceiptQuery,
16};
17
18mod any;
19mod cost;
20mod execute;
21pub(super) mod payment_transaction;
22mod protobuf;
23
24pub(crate) use any::AnyQueryData;
25pub use any::{
26 AnyQuery,
27 AnyQueryResponse,
28};
29pub(crate) use execute::{
30 response_header,
31 QueryExecute,
32};
33pub(crate) use protobuf::ToQueryProtobuf;
34
35#[derive(Debug, Default)]
37pub struct Query<D>
38where
39 D: QueryExecute,
40{
41 pub(crate) data: D,
42 pub(crate) payment: PaymentTransaction,
43}
44
45impl<D> Query<D>
46where
47 D: QueryExecute + Default,
48{
49 #[inline]
51 #[must_use]
52 pub fn new() -> Self {
53 Self::default()
54 }
55}
56
57impl<D> Query<D>
58where
59 D: QueryExecute,
60{
61 #[must_use]
66 pub fn get_node_account_ids(&self) -> Option<&[AccountId]> {
67 self.payment.get_node_account_ids()
68 }
69
70 pub fn node_account_ids(&mut self, ids: impl IntoIterator<Item = AccountId>) -> &mut Self {
75 self.payment.node_account_ids(ids);
76 self
77 }
78
79 #[must_use]
84 pub fn get_payment_amount(&self) -> Option<Hbar> {
85 self.payment.get_amount()
86 }
87
88 pub fn payment_amount(&mut self, amount: Hbar) -> &mut Self {
93 self.payment.amount(amount);
94 self
95 }
96
97 #[must_use]
99 pub fn get_max_amount(&self) -> Option<Hbar> {
100 self.payment.get_max_amount()
101 }
102
103 pub fn max_payment_amount(&mut self, max: impl Into<Option<Hbar>>) -> &mut Self {
116 self.payment.max_amount(max);
117 self
118 }
119
120 #[must_use]
122 pub fn get_payment_transaction_valid_duration(&self) -> Option<Duration> {
123 self.payment.get_transaction_valid_duration()
124 }
125
126 pub fn payment_transaction_valid_duration(&mut self, duration: Duration) -> &mut Self {
130 self.payment.transaction_valid_duration(duration);
131 self
132 }
133
134 #[must_use]
137 pub fn get_max_payment_transaction_fee(&self) -> Option<Hbar> {
138 self.payment.get_max_transaction_fee()
139 }
140
141 pub fn max_payment_transaction_fee(&mut self, fee: Hbar) -> &mut Self {
146 self.payment.max_transaction_fee(fee);
147 self
148 }
149
150 #[must_use]
152 pub fn get_payment_transaction_memo(&self) -> &str {
153 self.payment.get_transaction_memo()
154 }
155
156 pub fn payment_transaction_memo(&mut self, memo: impl AsRef<str>) -> &mut Self {
160 self.payment.transaction_memo(memo);
161 self
162 }
163
164 #[must_use]
167 pub fn get_payment_transaction_id(&self) -> Option<TransactionId> {
168 self.payment.get_transaction_id()
169 }
170
171 pub fn payment_transaction_id(&mut self, id: TransactionId) -> &mut Self {
176 self.payment.transaction_id(id);
177 self
178 }
179
180 pub async fn get_cost(&self, client: &Client) -> crate::Result<Hbar> {
182 self.get_cost_with_optional_timeout(client, None).await
183 }
184
185 pub(crate) async fn get_cost_with_optional_timeout(
186 &self,
187 client: &Client,
188 timeout: Option<std::time::Duration>,
189 ) -> crate::Result<Hbar> {
190 if !self.data.is_payment_required() {
191 return Ok(Hbar::ZERO);
192 }
193
194 QueryCost::new(self).execute(client, timeout).await
195 }
196
197 pub async fn get_cost_with_timeout(
199 &self,
200 client: &Client,
201 timeout: std::time::Duration,
202 ) -> crate::Result<Hbar> {
203 self.get_cost_with_optional_timeout(client, Some(timeout)).await
204 }
205}
206
207impl<D> Query<D>
208where
209 D: QueryExecute,
210{
211 #[allow(clippy::missing_errors_doc)]
214 pub async fn execute(&mut self, client: &Client) -> crate::Result<D::Response> {
215 self.execute_with_optional_timeout(client, None).await
216 }
217
218 pub(crate) async fn execute_with_optional_timeout(
220 &mut self,
221 client: &Client,
222 timeout: Option<std::time::Duration>,
223 ) -> crate::Result<D::Response> {
224 fn recurse_receipt(
225 transaction_id: &TransactionId,
226 client: Client,
227 timeout: Option<std::time::Duration>,
228 ) -> BoxFuture<'_, ()> {
229 Box::pin(async move {
230 let _ = TransactionReceiptQuery::new()
231 .transaction_id(*transaction_id)
232 .execute_with_optional_timeout(&client, timeout)
233 .await;
234 })
235 }
236
237 if let Some(transaction_id) = self.data.transaction_id() {
239 if self.data.is_payment_required() {
240 let client = client.clone();
241 recurse_receipt(&transaction_id, client, timeout).await;
242 }
243 }
244
245 if self.payment.get_amount().is_none() && self.data.is_payment_required() {
246 let cost = QueryCost::new(self).execute(client, None).await?;
249
250 if self.payment.get_max_amount().is_none() {
251 self.payment.max_amount(client.default_max_query_payment());
253 }
254
255 if let Some(max_amount) = self.payment.get_max_amount() {
256 if cost > max_amount {
257 return Err(Error::MaxQueryPaymentExceeded {
258 query_cost: cost,
259 max_query_payment: max_amount,
260 });
261 }
262 }
263
264 self.payment.amount(cost);
265 }
266
267 if self.data.is_payment_required() {
268 self.payment.freeze_with(client)?;
269 }
270
271 execute(client, self, timeout).await
272 }
273
274 #[allow(clippy::missing_errors_doc)]
277 pub async fn execute_with_timeout(
278 &mut self,
279 client: &Client,
280 timeout: std::time::Duration,
281 ) -> crate::Result<D::Response> {
282 self.execute_with_optional_timeout(client, Some(timeout)).await
283 }
284}