hiero_sdk/query/
execute.rs1use std::fmt::Debug;
4
5use hiero_sdk_proto::services;
6use tonic::transport::Channel;
7
8use crate::entity_id::ValidateChecksums;
9use crate::execute::Execute;
10use crate::query::{
11 AnyQueryData,
12 ToQueryProtobuf,
13};
14use crate::{
15 AccountId,
16 BoxGrpcFuture,
17 Error,
18 FromProtobuf,
19 Hbar,
20 Query,
21 Status,
22 TransactionId,
23};
24
25pub trait QueryExecute:
27 Sync + Send + Into<AnyQueryData> + Clone + Debug + ToQueryProtobuf + ValidateChecksums
28{
29 type Response: FromProtobuf<services::response::Response>;
30
31 fn is_payment_required(&self) -> bool {
33 true
34 }
35
36 fn map_cost(&self, cost: Hbar) -> Hbar {
38 cost
39 }
40
41 fn should_retry_pre_check(&self, _status: Status) -> bool {
44 false
45 }
46
47 #[allow(unused_variables)]
49 fn should_retry(&self, response: &services::Response) -> bool {
50 false
51 }
52
53 fn transaction_id(&self) -> Option<TransactionId> {
55 None
56 }
57
58 fn make_response(
59 &self,
60 response: services::response::Response,
61 ) -> crate::Result<Self::Response> {
62 <Self::Response as FromProtobuf<services::response::Response>>::from_protobuf(response)
63 }
64
65 fn execute(
67 &self,
68 channel: Channel,
69 request: services::Query,
70 ) -> BoxGrpcFuture<'_, services::Response>;
71}
72
73impl<D> Execute for Query<D>
74where
75 D: QueryExecute,
76{
77 type GrpcRequest = services::Query;
78
79 type GrpcResponse = services::Response;
80
81 type Response = D::Response;
82
83 type Context = ();
84
85 fn node_account_ids(&self) -> Option<&[AccountId]> {
86 self.payment.node_account_ids()
87 }
88
89 fn transaction_id(&self) -> Option<TransactionId> {
90 self.payment.transaction_id()
91 }
92
93 fn requires_transaction_id(&self) -> bool {
94 self.data.is_payment_required()
95 }
96
97 fn operator_account_id(&self) -> Option<&AccountId> {
98 self.payment.operator_account_id()
99 }
100
101 fn should_retry_pre_check(&self, status: Status) -> bool {
102 self.data.should_retry_pre_check(status)
103 }
104
105 fn should_retry(&self, response: &Self::GrpcResponse) -> bool {
106 self.data.should_retry(response)
107 }
108
109 fn make_request(
110 &self,
111 transaction_id: Option<&TransactionId>,
112 node_account_id: AccountId,
113 ) -> crate::Result<(Self::GrpcRequest, Self::Context)> {
114 let payment = if self.data.is_payment_required() {
115 Some(self.payment.make_request(transaction_id, node_account_id)?.0)
116 } else {
117 None
118 };
119
120 let header = services::QueryHeader { response_type: 0, payment };
121
122 Ok((self.data.to_query_protobuf(header), ()))
123 }
124
125 fn execute(
126 &self,
127 channel: Channel,
128 request: Self::GrpcRequest,
129 ) -> BoxGrpcFuture<'_, Self::GrpcResponse> {
130 self.data.execute(channel, request)
131 }
132
133 fn make_response(
134 &self,
135 response: Self::GrpcResponse,
136 _context: Self::Context,
137 _node_account_id: AccountId,
138 _transaction_id: Option<&TransactionId>,
139 ) -> crate::Result<Self::Response> {
140 pb_getf!(response, response).and_then(|response| self.data.make_response(response))
141 }
142
143 fn make_error_pre_check(
144 &self,
145 status: crate::Status,
146 transaction_id: Option<&TransactionId>,
147 _response: Self::GrpcResponse,
148 ) -> crate::Error {
149 if let Some(transaction_id) = self.data.transaction_id() {
150 crate::Error::QueryPreCheckStatus { status, transaction_id: Box::new(transaction_id) }
151 } else if let Some(transaction_id) = transaction_id {
152 crate::Error::QueryPaymentPreCheckStatus {
153 status,
154 transaction_id: Box::new(*transaction_id),
155 }
156 } else {
157 crate::Error::QueryNoPaymentPreCheckStatus { status }
158 }
159 }
160
161 fn response_pre_check_status(response: &Self::GrpcResponse) -> crate::Result<i32> {
162 Ok(response_header(&response.response)?.node_transaction_precheck_code)
163 }
164}
165
166impl<D: QueryExecute + ValidateChecksums> ValidateChecksums for Query<D> {
167 fn validate_checksums(&self, ledger_id: &crate::ledger_id::RefLedgerId) -> Result<(), Error> {
168 self.data.validate_checksums(ledger_id)?;
169 self.payment.validate_checksums(ledger_id)
170 }
171}
172
173pub(crate) fn response_header(
174 response: &Option<services::response::Response>,
175) -> crate::Result<&services::ResponseHeader> {
176 use services::response::Response::*;
177
178 let header = match response {
179 Some(CryptogetAccountBalance(response)) => &response.header,
180 Some(GetByKey(response)) => &response.header,
181 Some(GetBySolidityId(response)) => &response.header,
182 Some(ContractCallLocal(response)) => &response.header,
183 Some(ContractGetBytecodeResponse(response)) => &response.header,
184 Some(ContractGetInfo(response)) => &response.header,
185 Some(ContractGetRecordsResponse(response)) => &response.header,
186 Some(CryptoGetAccountRecords(response)) => &response.header,
187 Some(CryptoGetInfo(response)) => &response.header,
188 Some(CryptoGetLiveHash(response)) => &response.header,
189 Some(CryptoGetProxyStakers(response)) => &response.header,
190 Some(FileGetContents(response)) => &response.header,
191 Some(FileGetInfo(response)) => &response.header,
192 Some(TransactionGetReceipt(response)) => &response.header,
193 Some(TransactionGetRecord(response)) => &response.header,
194 Some(TransactionGetFastRecord(response)) => &response.header,
195 Some(ConsensusGetTopicInfo(response)) => &response.header,
196 Some(NetworkGetVersionInfo(response)) => &response.header,
197 Some(TokenGetInfo(response)) => &response.header,
198 Some(ScheduleGetInfo(response)) => &response.header,
199 Some(TokenGetAccountNftInfos(response)) => &response.header,
200 Some(TokenGetNftInfo(response)) => &response.header,
201 Some(TokenGetNftInfos(response)) => &response.header,
202 Some(NetworkGetExecutionTime(response)) => &response.header,
203 Some(AccountDetails(response)) => &response.header,
204 None => &None,
205 };
206
207 header.as_ref().ok_or_else(|| Error::from_protobuf("unexpected missing `header` in `Response`"))
208}