1use std::{
14 collections::VecDeque,
15 fmt::{Debug, Display},
16 num::{NonZeroUsize, TryFromIntError},
17};
18
19use hotshot_types::traits::{block_contents::BlockHeader, node_implementation::NodeType};
20use serde::{Deserialize, Serialize};
21use tide_disco::StatusCode;
22use time::format_description::well_known::Rfc3339;
23
24use super::{
25 errors::{BadQuery, ExplorerAPIError, InvalidLimit, NotFound, QueryError, Unimplemented},
26 monetary_value::MonetaryValue,
27 traits::{ExplorerHeader, ExplorerTransaction},
28};
29use crate::{
30 availability::{
31 BlockQueryData, NamespaceId, QueryableHeader, QueryablePayload, TransactionHash,
32 },
33 node::BlockHash,
34 types::HeightIndexed,
35 Header, Payload, Resolvable, Transaction,
36};
37
38#[derive(Debug, Clone, PartialEq, Eq)]
46pub enum BlockIdentifier<Types: NodeType> {
47 Latest,
48 Height(usize),
49 Hash(BlockHash<Types>),
50}
51
52impl<Types: NodeType> Display for BlockIdentifier<Types> {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 match self {
55 BlockIdentifier::Latest => write!(f, "latest"),
56 BlockIdentifier::Height(height) => write!(f, "{height}"),
57 BlockIdentifier::Hash(hash) => write!(f, "{hash}"),
58 }
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum TransactionIdentifier<Types: NodeType> {
76 Latest,
77 HeightAndOffset(usize, usize),
78 Hash(TransactionHash<Types>),
79}
80
81impl<Types: NodeType> Display for TransactionIdentifier<Types> {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 TransactionIdentifier::Latest => write!(f, "latest"),
85 TransactionIdentifier::HeightAndOffset(height, offset) => {
86 write!(f, "{height} {offset}")
87 },
88 TransactionIdentifier::Hash(hash) => write!(f, "{hash}"),
89 }
90 }
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
105pub struct BlockRange<Types: NodeType> {
106 pub target: BlockIdentifier<Types>,
107 pub num_blocks: NonZeroUsize,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
117pub struct TransactionRange<Types: NodeType> {
118 pub target: TransactionIdentifier<Types>,
119 pub num_transactions: NonZeroUsize,
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub struct Timestamp(pub time::OffsetDateTime);
130
131impl Serialize for Timestamp {
132 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
135 let formatted = self.0.format(&Rfc3339).map_err(serde::ser::Error::custom)?;
136 formatted.serialize(serializer)
137 }
138}
139
140impl<'de> Deserialize<'de> for Timestamp {
141 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
144 let s = String::deserialize(deserializer)?;
145 let dt = time::OffsetDateTime::parse(&s, &Rfc3339).map_err(serde::de::Error::custom)?;
146 Ok(Timestamp(dt))
147 }
148}
149
150pub type WalletAddress<Types> = <Header<Types> as ExplorerHeader<Types>>::WalletAddress;
151pub type ProposerId<Types> = <Header<Types> as ExplorerHeader<Types>>::ProposerId;
152pub type BalanceAmount<Types> = <Header<Types> as ExplorerHeader<Types>>::BalanceAmount;
153
154#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
157#[serde(bound = "")]
158pub struct BlockDetail<Types: NodeType>
159where
160 Header<Types>: ExplorerHeader<Types>,
161{
162 pub hash: BlockHash<Types>,
163 pub height: u64,
164 pub time: Timestamp,
165 pub num_transactions: u64,
166 pub proposer_id: ProposerId<Types>,
167 pub fee_recipient: WalletAddress<Types>,
168 pub size: u64,
169 pub block_reward: Vec<MonetaryValue>,
170}
171
172impl<Types: NodeType> TryFrom<BlockQueryData<Types>> for BlockDetail<Types>
173where
174 BlockQueryData<Types>: HeightIndexed,
175 Payload<Types>: QueryablePayload<Types>,
176 Header<Types>: BlockHeader<Types> + ExplorerHeader<Types>,
177 BalanceAmount<Types>: Into<MonetaryValue>,
178{
179 type Error = TimestampConversionError;
180
181 fn try_from(value: BlockQueryData<Types>) -> Result<Self, Self::Error> {
182 let seconds = i64::try_from(value.header.timestamp())?;
183
184 Ok(Self {
185 hash: value.hash(),
186 height: value.height(),
187 time: Timestamp(time::OffsetDateTime::from_unix_timestamp(seconds)?),
188 num_transactions: value.num_transactions,
189 proposer_id: value.header().proposer_id(),
190 fee_recipient: value.header().fee_info_account(),
191 size: value.size,
192 block_reward: vec![value.header().fee_info_balance().into()],
193 })
194 }
195}
196
197#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
201#[serde(bound = "")]
202pub struct BlockSummary<Types: NodeType>
203where
204 Header<Types>: ExplorerHeader<Types>,
205{
206 pub hash: BlockHash<Types>,
207 pub height: u64,
208 pub proposer_id: ProposerId<Types>,
209 pub num_transactions: u64,
210 pub size: u64,
211 pub time: Timestamp,
212}
213
214#[derive(Debug, PartialEq, Eq)]
219pub enum TimestampConversionError {
220 TimeError(time::error::ComponentRange),
221 IntError(TryFromIntError),
222}
223
224impl Display for TimestampConversionError {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 match self {
227 TimestampConversionError::TimeError(err) => write!(f, "{err:?}"),
228 TimestampConversionError::IntError(err) => write!(f, "{err:?}"),
229 }
230 }
231}
232
233impl std::error::Error for TimestampConversionError {
234 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
235 match self {
236 TimestampConversionError::TimeError(err) => Some(err),
237 TimestampConversionError::IntError(err) => Some(err),
238 }
239 }
240}
241
242impl From<time::error::ComponentRange> for TimestampConversionError {
243 fn from(value: time::error::ComponentRange) -> Self {
244 Self::TimeError(value)
245 }
246}
247
248impl From<TryFromIntError> for TimestampConversionError {
249 fn from(value: TryFromIntError) -> Self {
250 Self::IntError(value)
251 }
252}
253
254impl From<TimestampConversionError> for crate::QueryError {
255 fn from(value: TimestampConversionError) -> Self {
256 Self::Error {
257 message: format!("{value:?}"),
258 }
259 }
260}
261
262impl<Types: NodeType> TryFrom<BlockQueryData<Types>> for BlockSummary<Types>
263where
264 BlockQueryData<Types>: HeightIndexed,
265 Payload<Types>: QueryablePayload<Types>,
266 Header<Types>: BlockHeader<Types> + ExplorerHeader<Types>,
267{
268 type Error = TimestampConversionError;
269
270 fn try_from(value: BlockQueryData<Types>) -> Result<Self, Self::Error> {
271 let seconds = i64::try_from(value.header.timestamp())?;
272
273 Ok(Self {
274 hash: value.hash(),
275 height: value.height(),
276 proposer_id: value.header().proposer_id(),
277 num_transactions: value.num_transactions,
278 size: value.size,
279 time: Timestamp(time::OffsetDateTime::from_unix_timestamp(seconds)?),
280 })
281 }
282}
283
284#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
293pub struct FeeAttribution {
294 pub target: String,
295 pub fees: Vec<MonetaryValue>,
296}
297
298#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
301#[serde(bound = "")]
302pub struct TransactionDetail<Types: NodeType> {
303 pub hash: TransactionHash<Types>,
304 pub height: u64,
305 pub block_confirmed: bool,
306 pub offset: u64,
307 pub num_transactions: u64,
308 pub size: u64,
309 pub time: Timestamp,
310 pub sequencing_fees: Vec<MonetaryValue>,
311 pub fee_details: Vec<FeeAttribution>,
312}
313
314#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
318#[serde(bound = "")]
319pub struct TransactionDetailResponse<Types: NodeType> {
320 pub details: TransactionDetail<Types>,
321 pub data: Vec<Transaction<Types>>,
322}
323
324#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
329#[serde(bound = "")]
330pub struct TransactionSummary<Types: NodeType>
331where
332 Header<Types>: ExplorerHeader<Types>,
333{
334 pub hash: TransactionHash<Types>,
335 pub rollups: Vec<NamespaceId<Types>>,
336 pub height: u64,
337 pub offset: u64,
338 pub num_transactions: u64,
339 pub time: Timestamp,
340}
341
342impl<Types: NodeType>
343 TryFrom<(
344 &BlockQueryData<Types>,
345 usize,
346 <Types as NodeType>::Transaction,
347 )> for TransactionSummary<Types>
348where
349 BlockQueryData<Types>: HeightIndexed,
350 Payload<Types>: QueryablePayload<Types>,
351 Header<Types>: QueryableHeader<Types> + ExplorerHeader<Types>,
352 Transaction<Types>: ExplorerTransaction<Types>,
353{
354 type Error = TimestampConversionError;
355
356 fn try_from(
357 (block, offset, transaction): (
358 &BlockQueryData<Types>,
359 usize,
360 <Types as NodeType>::Transaction,
361 ),
362 ) -> Result<Self, Self::Error> {
363 let seconds = i64::try_from(block.header.timestamp())?;
364
365 Ok(Self {
366 hash: transaction.commitment(),
367 height: block.height(),
368 offset: offset as u64,
369 num_transactions: block.num_transactions,
370 time: Timestamp(time::OffsetDateTime::from_unix_timestamp(seconds)?),
371 rollups: vec![transaction.namespace_id()],
372 })
373 }
374}
375
376impl<Types: NodeType>
377 TryFrom<(
378 &BlockQueryData<Types>,
379 usize,
380 <Types as NodeType>::Transaction,
381 )> for TransactionDetailResponse<Types>
382where
383 BlockQueryData<Types>: HeightIndexed,
384 Payload<Types>: QueryablePayload<Types>,
385 Header<Types>: QueryableHeader<Types> + ExplorerHeader<Types>,
386 <Types as NodeType>::Transaction: ExplorerTransaction<Types>,
387{
388 type Error = TimestampConversionError;
389
390 fn try_from(
391 (block, offset, transaction): (
392 &BlockQueryData<Types>,
393 usize,
394 <Types as NodeType>::Transaction,
395 ),
396 ) -> Result<Self, Self::Error> {
397 let seconds = i64::try_from(block.header.timestamp())?;
398
399 Ok(Self {
400 details: TransactionDetail {
401 hash: transaction.commitment(),
402 height: block.height(),
403 block_confirmed: true,
404 offset: offset as u64,
405 num_transactions: block.num_transactions,
406 size: transaction.payload_size(),
407 time: Timestamp(time::OffsetDateTime::from_unix_timestamp(seconds)?),
408 sequencing_fees: vec![],
409 fee_details: vec![],
410 },
411 data: vec![transaction],
412 })
413 }
414}
415
416#[derive(Debug, PartialEq, Eq)]
420pub struct GetBlockSummariesRequest<Types: NodeType>(pub BlockRange<Types>);
421
422#[derive(Debug, Deserialize, Serialize)]
425#[serde(bound = "")]
426pub enum TransactionSummaryFilter<Types>
427where
428 Types: NodeType,
429 Header<Types>: QueryableHeader<Types>,
430{
431 None,
432 RollUp(NamespaceId<Types>),
433 Block(usize),
434}
435
436#[derive(Debug)]
441pub struct GetTransactionSummariesRequest<Types>
442where
443 Types: NodeType,
444 Header<Types>: QueryableHeader<Types>,
445{
446 pub range: TransactionRange<Types>,
447 pub filter: TransactionSummaryFilter<Types>,
448}
449
450impl<Types> Default for GetTransactionSummariesRequest<Types>
451where
452 Types: NodeType,
453 Header<Types>: QueryableHeader<Types>,
454{
455 fn default() -> Self {
456 Self {
457 range: TransactionRange {
458 target: TransactionIdentifier::Latest,
459 num_transactions: NonZeroUsize::new(20).unwrap(),
460 },
461 filter: TransactionSummaryFilter::None,
462 }
463 }
464}
465
466#[derive(Debug, Serialize, Deserialize)]
470pub struct GenesisOverview {
471 pub rollups: u64,
472 pub transactions: u64,
473 pub blocks: u64,
474 }
476
477#[derive(Debug, Serialize, Deserialize)]
487pub struct ExplorerHistograms {
488 pub block_time: VecDeque<Option<u64>>,
489 pub block_size: VecDeque<Option<u64>>,
490 pub block_transactions: VecDeque<u64>,
491 pub block_heights: VecDeque<u64>,
492}
493
494#[derive(Debug, Serialize, Deserialize)]
503#[serde(bound = "")]
504pub struct ExplorerSummary<Types: NodeType>
505where
506 Header<Types>: ExplorerHeader<Types>,
507 Transaction<Types>: ExplorerTransaction<Types>,
508{
509 pub latest_block: BlockDetail<Types>,
510 pub genesis_overview: GenesisOverview,
511 pub latest_blocks: Vec<BlockSummary<Types>>,
512 pub latest_transactions: Vec<TransactionSummary<Types>>,
513 pub histograms: ExplorerHistograms,
515}
516
517#[derive(Debug, Serialize, Deserialize)]
521#[serde(bound = "")]
522pub struct SearchResult<Types: NodeType>
523where
524 Header<Types>: ExplorerHeader<Types>,
525 Transaction<Types>: ExplorerTransaction<Types>,
526{
527 pub blocks: Vec<BlockSummary<Types>>,
528 pub transactions: Vec<TransactionSummary<Types>>,
529}
530
531#[derive(Debug, Clone, Serialize, Deserialize)]
534#[serde(untagged)]
535pub enum GetBlockDetailError {
536 Unimplemented(Unimplemented),
537 BlockNotFound(NotFound),
538 QueryError(QueryError),
539}
540
541impl GetBlockDetailError {
542 pub fn status(&self) -> StatusCode {
543 match self {
544 GetBlockDetailError::Unimplemented(err) => err.status(),
545 GetBlockDetailError::QueryError(err) => err.status(),
546 GetBlockDetailError::BlockNotFound(err) => err.status(),
547 }
548 }
549}
550
551impl Display for GetBlockDetailError {
552 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
553 match self {
554 GetBlockDetailError::Unimplemented(err) => write!(f, "{err}"),
555 GetBlockDetailError::QueryError(err) => write!(f, "{err}"),
556 GetBlockDetailError::BlockNotFound(err) => write!(f, "{err}"),
557 }
558 }
559}
560
561impl ExplorerAPIError for GetBlockDetailError {
562 fn code(&self) -> &str {
563 match self {
564 GetBlockDetailError::Unimplemented(err) => err.code(),
565 GetBlockDetailError::QueryError(err) => err.code(),
566 GetBlockDetailError::BlockNotFound(err) => err.code(),
567 }
568 }
569}
570
571impl std::error::Error for GetBlockDetailError {
572 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
573 match self {
574 GetBlockDetailError::Unimplemented(err) => Some(err),
575 GetBlockDetailError::QueryError(err) => Some(err),
576 _ => None,
577 }
578 }
579}
580
581impl From<crate::QueryError> for GetBlockDetailError {
582 fn from(value: crate::QueryError) -> Self {
583 GetBlockDetailError::QueryError(QueryError { error: value })
584 }
585}
586
587#[derive(Debug, Clone, Serialize, Deserialize)]
590#[serde(untagged)]
591pub enum GetBlockSummariesError {
592 Unimplemented(Unimplemented),
593 InvalidLimit(InvalidLimit),
594 TargetNotFound(NotFound),
595 QueryError(QueryError),
596}
597
598impl GetBlockSummariesError {
599 pub fn status(&self) -> StatusCode {
600 match self {
601 GetBlockSummariesError::Unimplemented(err) => err.status(),
602 GetBlockSummariesError::InvalidLimit(err) => err.status(),
603 GetBlockSummariesError::QueryError(err) => err.status(),
604 GetBlockSummariesError::TargetNotFound(err) => err.status(),
605 }
606 }
607}
608
609impl Display for GetBlockSummariesError {
610 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
611 match self {
612 GetBlockSummariesError::Unimplemented(err) => write!(f, "{err}"),
613 GetBlockSummariesError::InvalidLimit(err) => write!(f, "{err}"),
614 GetBlockSummariesError::QueryError(err) => write!(f, "{err}"),
615 GetBlockSummariesError::TargetNotFound(err) => write!(f, "{err}"),
616 }
617 }
618}
619
620impl ExplorerAPIError for GetBlockSummariesError {
621 fn code(&self) -> &str {
622 match self {
623 GetBlockSummariesError::Unimplemented(err) => err.code(),
624 GetBlockSummariesError::InvalidLimit(err) => err.code(),
625 GetBlockSummariesError::QueryError(err) => err.code(),
626 GetBlockSummariesError::TargetNotFound(err) => err.code(),
627 }
628 }
629}
630
631impl std::error::Error for GetBlockSummariesError {
632 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
633 match self {
634 GetBlockSummariesError::Unimplemented(err) => Some(err),
635 GetBlockSummariesError::InvalidLimit(err) => Some(err),
636 GetBlockSummariesError::QueryError(err) => Some(err),
637 _ => None,
638 }
639 }
640}
641
642impl From<crate::QueryError> for GetBlockSummariesError {
643 fn from(value: crate::QueryError) -> Self {
644 GetBlockSummariesError::QueryError(QueryError { error: value })
645 }
646}
647
648#[derive(Debug, Clone, Serialize, Deserialize)]
651#[serde(untagged)]
652pub enum GetTransactionDetailError {
653 Unimplemented(Unimplemented),
654 TransactionNotFound(NotFound),
655 QueryError(QueryError),
656}
657
658impl GetTransactionDetailError {
659 pub fn status(&self) -> StatusCode {
660 match self {
661 GetTransactionDetailError::Unimplemented(err) => err.status(),
662 GetTransactionDetailError::QueryError(err) => err.status(),
663 GetTransactionDetailError::TransactionNotFound(err) => err.status(),
664 }
665 }
666}
667
668impl Display for GetTransactionDetailError {
669 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
670 match self {
671 GetTransactionDetailError::Unimplemented(err) => write!(f, "{err}"),
672 GetTransactionDetailError::QueryError(err) => write!(f, "{err}"),
673 GetTransactionDetailError::TransactionNotFound(err) => write!(f, "{err}"),
674 }
675 }
676}
677
678impl ExplorerAPIError for GetTransactionDetailError {
679 fn code(&self) -> &str {
680 match self {
681 GetTransactionDetailError::Unimplemented(err) => err.code(),
682 GetTransactionDetailError::QueryError(err) => err.code(),
683 GetTransactionDetailError::TransactionNotFound(err) => err.code(),
684 }
685 }
686}
687
688impl std::error::Error for GetTransactionDetailError {
689 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
690 match self {
691 GetTransactionDetailError::Unimplemented(err) => Some(err),
692 GetTransactionDetailError::QueryError(err) => Some(err),
693 _ => None,
694 }
695 }
696}
697
698impl From<crate::QueryError> for GetTransactionDetailError {
701 fn from(value: crate::QueryError) -> Self {
702 GetTransactionDetailError::QueryError(QueryError { error: value })
703 }
704}
705
706impl From<TimestampConversionError> for GetTransactionDetailError {
707 fn from(value: TimestampConversionError) -> Self {
708 GetTransactionDetailError::QueryError(QueryError {
709 error: value.into(),
710 })
711 }
712}
713
714#[derive(Debug, Clone, Serialize, Deserialize)]
717#[serde(untagged)]
718pub enum GetTransactionSummariesError {
719 Unimplemented(Unimplemented),
720 InvalidLimit(InvalidLimit),
721 TargetNotFound(NotFound),
722 QueryError(QueryError),
723}
724
725impl GetTransactionSummariesError {
726 pub fn status(&self) -> StatusCode {
727 match self {
728 GetTransactionSummariesError::Unimplemented(err) => err.status(),
729 GetTransactionSummariesError::InvalidLimit(err) => err.status(),
730 GetTransactionSummariesError::QueryError(err) => err.status(),
731 GetTransactionSummariesError::TargetNotFound(err) => err.status(),
732 }
733 }
734}
735
736impl Display for GetTransactionSummariesError {
737 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
738 match self {
739 GetTransactionSummariesError::Unimplemented(err) => write!(f, "{err}"),
740 GetTransactionSummariesError::InvalidLimit(err) => write!(f, "{err}"),
741 GetTransactionSummariesError::QueryError(err) => write!(f, "{err}"),
742 GetTransactionSummariesError::TargetNotFound(err) => write!(f, "{err}"),
743 }
744 }
745}
746
747impl ExplorerAPIError for GetTransactionSummariesError {
748 fn code(&self) -> &str {
749 match self {
750 GetTransactionSummariesError::Unimplemented(err) => err.code(),
751 GetTransactionSummariesError::InvalidLimit(err) => err.code(),
752 GetTransactionSummariesError::QueryError(err) => err.code(),
753 GetTransactionSummariesError::TargetNotFound(err) => err.code(),
754 }
755 }
756}
757
758impl std::error::Error for GetTransactionSummariesError {
759 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
760 match self {
761 GetTransactionSummariesError::Unimplemented(err) => Some(err),
762 GetTransactionSummariesError::InvalidLimit(err) => Some(err),
763 GetTransactionSummariesError::QueryError(err) => Some(err),
764 _ => None,
765 }
766 }
767}
768
769impl From<crate::QueryError> for GetTransactionSummariesError {
770 fn from(value: crate::QueryError) -> Self {
771 GetTransactionSummariesError::QueryError(QueryError { error: value })
772 }
773}
774
775#[derive(Debug, Clone, Serialize, Deserialize)]
778#[serde(untagged)]
779pub enum GetExplorerSummaryError {
780 Unimplemented(Unimplemented),
781 QueryError(QueryError),
782 GetBlockDetailError(GetBlockDetailError),
783 GetBlockSummariesError(GetBlockSummariesError),
784 GetTransactionSummariesError(GetTransactionSummariesError),
785}
786
787impl GetExplorerSummaryError {
788 pub fn status(&self) -> StatusCode {
789 match self {
790 GetExplorerSummaryError::QueryError(err) => err.status(),
791 GetExplorerSummaryError::Unimplemented(err) => err.status(),
792 GetExplorerSummaryError::GetBlockDetailError(err) => err.status(),
793 GetExplorerSummaryError::GetBlockSummariesError(err) => err.status(),
794 GetExplorerSummaryError::GetTransactionSummariesError(err) => err.status(),
795 }
796 }
797}
798
799impl Display for GetExplorerSummaryError {
800 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
801 match self {
802 GetExplorerSummaryError::QueryError(err) => write!(f, "{err}"),
803 GetExplorerSummaryError::Unimplemented(err) => write!(f, "{err}"),
804 GetExplorerSummaryError::GetBlockDetailError(err) => write!(f, "{err}"),
805 GetExplorerSummaryError::GetBlockSummariesError(err) => write!(f, "{err}"),
806 GetExplorerSummaryError::GetTransactionSummariesError(err) => write!(f, "{err}"),
807 }
808 }
809}
810
811impl ExplorerAPIError for GetExplorerSummaryError {
812 fn code(&self) -> &str {
813 match self {
814 GetExplorerSummaryError::QueryError(err) => err.code(),
815 GetExplorerSummaryError::Unimplemented(err) => err.code(),
816 GetExplorerSummaryError::GetBlockDetailError(err) => err.code(),
817 GetExplorerSummaryError::GetBlockSummariesError(err) => err.code(),
818 GetExplorerSummaryError::GetTransactionSummariesError(err) => err.code(),
819 }
820 }
821}
822
823impl std::error::Error for GetExplorerSummaryError {
824 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
825 match self {
826 GetExplorerSummaryError::Unimplemented(err) => Some(err),
827 GetExplorerSummaryError::QueryError(err) => Some(err),
828 GetExplorerSummaryError::GetBlockDetailError(err) => Some(err),
829 GetExplorerSummaryError::GetBlockSummariesError(err) => Some(err),
830 GetExplorerSummaryError::GetTransactionSummariesError(err) => Some(err),
831 }
832 }
833}
834
835impl From<crate::QueryError> for GetExplorerSummaryError {
838 fn from(value: crate::QueryError) -> Self {
839 GetExplorerSummaryError::QueryError(QueryError { error: value })
840 }
841}
842
843impl From<GetBlockDetailError> for GetExplorerSummaryError {
844 fn from(value: GetBlockDetailError) -> Self {
845 GetExplorerSummaryError::GetBlockDetailError(value)
846 }
847}
848
849impl From<GetBlockSummariesError> for GetExplorerSummaryError {
850 fn from(value: GetBlockSummariesError) -> Self {
851 GetExplorerSummaryError::GetBlockSummariesError(value)
852 }
853}
854
855impl From<GetTransactionSummariesError> for GetExplorerSummaryError {
856 fn from(value: GetTransactionSummariesError) -> Self {
857 GetExplorerSummaryError::GetTransactionSummariesError(value)
858 }
859}
860
861#[derive(Debug, Clone, Serialize, Deserialize)]
864#[serde(untagged)]
865pub enum GetSearchResultsError {
866 Unimplemented(Unimplemented),
867 QueryError(QueryError),
868 InvalidQuery(BadQuery),
869}
870
871impl GetSearchResultsError {
872 pub fn status(&self) -> StatusCode {
873 match self {
874 GetSearchResultsError::QueryError(err) => err.status(),
875 GetSearchResultsError::Unimplemented(err) => err.status(),
876 GetSearchResultsError::InvalidQuery(err) => err.status(),
877 }
878 }
879}
880
881impl Display for GetSearchResultsError {
882 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
883 match self {
884 GetSearchResultsError::QueryError(err) => write!(f, "{err}"),
885 GetSearchResultsError::Unimplemented(err) => write!(f, "{err}"),
886 GetSearchResultsError::InvalidQuery(err) => write!(f, "{err}"),
887 }
888 }
889}
890
891impl ExplorerAPIError for GetSearchResultsError {
892 fn code(&self) -> &str {
893 match self {
894 GetSearchResultsError::QueryError(err) => err.code(),
895 GetSearchResultsError::Unimplemented(err) => err.code(),
896 GetSearchResultsError::InvalidQuery(err) => err.code(),
897 }
898 }
899}
900
901impl std::error::Error for GetSearchResultsError {
902 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
903 match self {
904 GetSearchResultsError::Unimplemented(err) => Some(err),
905 GetSearchResultsError::QueryError(err) => Some(err),
906 GetSearchResultsError::InvalidQuery(err) => Some(err),
907 }
908 }
909}
910
911impl From<crate::QueryError> for GetSearchResultsError {
912 fn from(value: crate::QueryError) -> Self {
913 GetSearchResultsError::QueryError(QueryError { error: value })
914 }
915}