zingo_netutils/error.rs
1//! Error types for the [`Indexer`](super::Indexer) and
2//! `TransparentIndexer` traits.
3//!
4//! Each trait method has a dedicated error enum so callers can
5//! distinguish connection failures ([`GetClientError`]) from
6//! server-side gRPC errors ([`tonic::Status`]).
7
8/// Connection-level error returned by [`GrpcIndexer::new`](super::GrpcIndexer::new)
9/// and the connection phase of every trait method.
10///
11/// Callers can depend on:
12/// - `InvalidScheme` and `InvalidAuthority` are deterministic — retrying
13/// with the same URI will always fail.
14/// - `Transport` wraps a [`tonic::transport::Error`] and may be transient
15/// (e.g. DNS resolution, TCP connect timeout). Retrying may succeed.
16///
17/// ```
18/// use zingo_netutils::GetClientError;
19///
20/// let e = GetClientError::InvalidScheme;
21/// assert_eq!(e.to_string(), "bad uri: invalid scheme");
22///
23/// let e = GetClientError::InvalidAuthority;
24/// assert_eq!(e.to_string(), "bad uri: invalid authority");
25///
26/// // Transport variant accepts From<tonic::transport::Error>
27/// let _: fn(tonic::transport::Error) -> GetClientError = GetClientError::from;
28/// ```
29#[derive(Debug, thiserror::Error)]
30pub enum GetClientError {
31 #[error("bad uri: invalid scheme")]
32 InvalidScheme,
33
34 #[error("bad uri: invalid authority")]
35 InvalidAuthority,
36
37 #[error(transparent)]
38 Transport(#[from] tonic::transport::Error),
39}
40
41#[cfg(test)]
42mod get_client_error_tests {
43 use super::*;
44
45 #[test]
46 fn invalid_scheme() {
47 let e = GetClientError::InvalidScheme;
48 assert!(matches!(e, GetClientError::InvalidScheme));
49 assert_eq!(e.to_string(), "bad uri: invalid scheme");
50 }
51
52 #[test]
53 fn invalid_authority() {
54 let e = GetClientError::InvalidAuthority;
55 assert!(matches!(e, GetClientError::InvalidAuthority));
56 assert_eq!(e.to_string(), "bad uri: invalid authority");
57 }
58
59 #[test]
60 fn transport_from_conversion() {
61 // Verify the From impl exists at compile time.
62 let _: fn(tonic::transport::Error) -> GetClientError = GetClientError::from;
63 }
64}
65
66/// Error from the `get_info` (`GetLightdInfo`) RPC.
67///
68/// Callers can depend on:
69/// - `GetClientError` means the connection was never established.
70/// - `GetLightdInfoError` means the server received the request but
71/// returned a gRPC status (e.g. `Unavailable`, `Internal`).
72///
73/// ```
74/// use zingo_netutils::{GetClientError, GetInfoError};
75///
76/// let e = GetInfoError::from(GetClientError::InvalidScheme);
77/// assert!(matches!(e, GetInfoError::GetClientError(_)));
78///
79/// let e = GetInfoError::from(tonic::Status::internal("oops"));
80/// assert!(matches!(e, GetInfoError::GetLightdInfoError(_)));
81/// assert!(e.to_string().contains("oops"));
82/// ```
83#[derive(Debug, thiserror::Error)]
84pub enum GetInfoError {
85 #[error(transparent)]
86 GetClientError(#[from] GetClientError),
87
88 #[error("gRPC error: {0}")]
89 GetLightdInfoError(#[from] tonic::Status),
90}
91
92/// Error from the `get_latest_block` (`GetLatestBlock`) RPC.
93///
94/// Callers can depend on:
95/// - `GetClientError` means the connection was never established.
96/// - `GetLatestBlockError` means the server returned a gRPC status.
97///
98/// ```
99/// use zingo_netutils::{GetClientError, GetLatestBlockError};
100///
101/// let e = GetLatestBlockError::from(GetClientError::InvalidScheme);
102/// assert!(matches!(e, GetLatestBlockError::GetClientError(_)));
103///
104/// let e = GetLatestBlockError::from(tonic::Status::internal("oops"));
105/// assert!(matches!(e, GetLatestBlockError::GetLatestBlockError(_)));
106/// ```
107#[derive(Debug, thiserror::Error)]
108pub enum GetLatestBlockError {
109 #[error(transparent)]
110 GetClientError(#[from] GetClientError),
111
112 #[error("gRPC error: {0}")]
113 GetLatestBlockError(#[from] tonic::Status),
114}
115
116/// Error from the `send_transaction` (`SendTransaction`) RPC.
117///
118/// Callers can depend on:
119/// - `GetClientError` means the connection was never established.
120/// - `SendTransactionError` means the server returned a gRPC status
121/// before evaluating the transaction.
122/// - `SendRejected` means the server received and evaluated the
123/// transaction but rejected it (e.g. duplicate, invalid). The string
124/// contains the server's rejection reason. This is **not** retryable
125/// with the same transaction bytes.
126///
127/// ```
128/// use zingo_netutils::{GetClientError, SendTransactionError};
129///
130/// let e = SendTransactionError::from(GetClientError::InvalidScheme);
131/// assert!(matches!(e, SendTransactionError::GetClientError(_)));
132///
133/// let e = SendTransactionError::from(tonic::Status::internal("oops"));
134/// assert!(matches!(e, SendTransactionError::SendTransactionError(_)));
135///
136/// let e = SendTransactionError::SendRejected("duplicate".into());
137/// assert!(matches!(e, SendTransactionError::SendRejected(_)));
138/// assert_eq!(e.to_string(), "send rejected: duplicate");
139/// ```
140#[derive(Debug, thiserror::Error)]
141pub enum SendTransactionError {
142 #[error(transparent)]
143 GetClientError(#[from] GetClientError),
144
145 #[error("gRPC error: {0}")]
146 SendTransactionError(#[from] tonic::Status),
147
148 #[error("send rejected: {0}")]
149 SendRejected(String),
150}
151
152/// Error from the `get_tree_state` (`GetTreeState`) RPC.
153///
154/// Callers can depend on:
155/// - `GetClientError` means the connection was never established.
156/// - `GetTreeStateError` means the server returned a gRPC status
157/// (e.g. the requested block does not exist).
158///
159/// ```
160/// use zingo_netutils::{GetClientError, GetTreeStateError};
161///
162/// let e = GetTreeStateError::from(GetClientError::InvalidScheme);
163/// assert!(matches!(e, GetTreeStateError::GetClientError(_)));
164///
165/// let e = GetTreeStateError::from(tonic::Status::not_found("no such block"));
166/// assert!(matches!(e, GetTreeStateError::GetTreeStateError(_)));
167/// ```
168#[derive(Debug, thiserror::Error)]
169pub enum GetTreeStateError {
170 #[error(transparent)]
171 GetClientError(#[from] GetClientError),
172
173 #[error("gRPC error: {0}")]
174 GetTreeStateError(#[from] tonic::Status),
175}
176
177/// Error from the `get_block` (`GetBlock`) RPC.
178///
179/// Callers can depend on:
180/// - `GetClientError` means the connection was never established.
181/// - `GetBlockError` means the server returned a gRPC status
182/// (e.g. the requested block does not exist).
183///
184/// ```
185/// use zingo_netutils::{GetClientError, GetBlockError};
186///
187/// let e = GetBlockError::from(GetClientError::InvalidScheme);
188/// assert!(matches!(e, GetBlockError::GetClientError(_)));
189///
190/// let e = GetBlockError::from(tonic::Status::not_found("no such block"));
191/// assert!(matches!(e, GetBlockError::GetBlockError(_)));
192/// ```
193#[derive(Debug, thiserror::Error)]
194pub enum GetBlockError {
195 #[error(transparent)]
196 GetClientError(#[from] GetClientError),
197
198 #[error("gRPC error: {0}")]
199 GetBlockError(#[from] tonic::Status),
200}
201
202/// Error from the deprecated `get_block_nullifiers` (`GetBlockNullifiers`) RPC.
203///
204/// Callers can depend on:
205/// - `GetClientError` means the connection was never established.
206/// - `GetBlockNullifiersError` means the server returned a gRPC status.
207///
208/// ```
209/// use zingo_netutils::{GetClientError, GetBlockNullifiersError};
210///
211/// let e = GetBlockNullifiersError::from(GetClientError::InvalidScheme);
212/// assert!(matches!(e, GetBlockNullifiersError::GetClientError(_)));
213///
214/// let e = GetBlockNullifiersError::from(tonic::Status::internal("oops"));
215/// assert!(matches!(e, GetBlockNullifiersError::GetBlockNullifiersError(_)));
216/// ```
217#[derive(Debug, thiserror::Error)]
218pub enum GetBlockNullifiersError {
219 #[error(transparent)]
220 GetClientError(#[from] GetClientError),
221
222 #[error("gRPC error: {0}")]
223 GetBlockNullifiersError(#[from] tonic::Status),
224}
225
226/// Error from the `get_block_range` (`GetBlockRange`) RPC.
227///
228/// Callers can depend on:
229/// - `GetClientError` means the connection was never established.
230/// - `GetBlockRangeError` means the server returned a gRPC status
231/// before or instead of streaming blocks.
232///
233/// ```
234/// use zingo_netutils::{GetClientError, GetBlockRangeError};
235///
236/// let e = GetBlockRangeError::from(GetClientError::InvalidScheme);
237/// assert!(matches!(e, GetBlockRangeError::GetClientError(_)));
238///
239/// let e = GetBlockRangeError::from(tonic::Status::internal("oops"));
240/// assert!(matches!(e, GetBlockRangeError::GetBlockRangeError(_)));
241/// ```
242#[derive(Debug, thiserror::Error)]
243pub enum GetBlockRangeError {
244 #[error(transparent)]
245 GetClientError(#[from] GetClientError),
246
247 #[error("gRPC error: {0}")]
248 GetBlockRangeError(#[from] tonic::Status),
249}
250
251/// Error from the deprecated `get_block_range_nullifiers` (`GetBlockRangeNullifiers`) RPC.
252///
253/// Callers can depend on:
254/// - `GetClientError` means the connection was never established.
255/// - `GetBlockRangeNullifiersError` means the server returned a gRPC status.
256///
257/// ```
258/// use zingo_netutils::{GetClientError, GetBlockRangeNullifiersError};
259///
260/// let e = GetBlockRangeNullifiersError::from(GetClientError::InvalidScheme);
261/// assert!(matches!(e, GetBlockRangeNullifiersError::GetClientError(_)));
262///
263/// let e = GetBlockRangeNullifiersError::from(tonic::Status::internal("oops"));
264/// assert!(matches!(e, GetBlockRangeNullifiersError::GetBlockRangeNullifiersError(_)));
265/// ```
266#[derive(Debug, thiserror::Error)]
267pub enum GetBlockRangeNullifiersError {
268 #[error(transparent)]
269 GetClientError(#[from] GetClientError),
270
271 #[error("gRPC error: {0}")]
272 GetBlockRangeNullifiersError(#[from] tonic::Status),
273}
274
275/// Error from the `get_transaction` (`GetTransaction`) RPC.
276///
277/// Callers can depend on:
278/// - `GetClientError` means the connection was never established.
279/// - `GetTransactionError` means the server returned a gRPC status
280/// (e.g. the transaction was not found).
281///
282/// ```
283/// use zingo_netutils::{GetClientError, GetTransactionError};
284///
285/// let e = GetTransactionError::from(GetClientError::InvalidScheme);
286/// assert!(matches!(e, GetTransactionError::GetClientError(_)));
287///
288/// let e = GetTransactionError::from(tonic::Status::not_found("no such tx"));
289/// assert!(matches!(e, GetTransactionError::GetTransactionError(_)));
290/// ```
291#[derive(Debug, thiserror::Error)]
292pub enum GetTransactionError {
293 #[error(transparent)]
294 GetClientError(#[from] GetClientError),
295
296 #[error("gRPC error: {0}")]
297 GetTransactionError(#[from] tonic::Status),
298}
299
300/// Error from the `get_mempool_tx` (`GetMempoolTx`) RPC.
301///
302/// Callers can depend on:
303/// - `GetClientError` means the connection was never established.
304/// - `GetMempoolTxError` means the server returned a gRPC status
305/// before or instead of streaming mempool transactions.
306///
307/// ```
308/// use zingo_netutils::{GetClientError, GetMempoolTxError};
309///
310/// let e = GetMempoolTxError::from(GetClientError::InvalidScheme);
311/// assert!(matches!(e, GetMempoolTxError::GetClientError(_)));
312///
313/// let e = GetMempoolTxError::from(tonic::Status::internal("oops"));
314/// assert!(matches!(e, GetMempoolTxError::GetMempoolTxError(_)));
315/// ```
316#[derive(Debug, thiserror::Error)]
317pub enum GetMempoolTxError {
318 #[error(transparent)]
319 GetClientError(#[from] GetClientError),
320
321 #[error("gRPC error: {0}")]
322 GetMempoolTxError(#[from] tonic::Status),
323}
324
325/// Error from the `get_mempool_stream` (`GetMempoolStream`) RPC.
326///
327/// Callers can depend on:
328/// - `GetClientError` means the connection was never established.
329/// - `GetMempoolStreamError` means the server returned a gRPC status
330/// before or instead of opening the stream.
331///
332/// ```
333/// use zingo_netutils::{GetClientError, GetMempoolStreamError};
334///
335/// let e = GetMempoolStreamError::from(GetClientError::InvalidScheme);
336/// assert!(matches!(e, GetMempoolStreamError::GetClientError(_)));
337///
338/// let e = GetMempoolStreamError::from(tonic::Status::internal("oops"));
339/// assert!(matches!(e, GetMempoolStreamError::GetMempoolStreamError(_)));
340/// ```
341#[derive(Debug, thiserror::Error)]
342pub enum GetMempoolStreamError {
343 #[error(transparent)]
344 GetClientError(#[from] GetClientError),
345
346 #[error("gRPC error: {0}")]
347 GetMempoolStreamError(#[from] tonic::Status),
348}
349
350/// Error from the `get_latest_tree_state` (`GetLatestTreeState`) RPC.
351///
352/// Callers can depend on:
353/// - `GetClientError` means the connection was never established.
354/// - `GetLatestTreeStateError` means the server returned a gRPC status.
355///
356/// ```
357/// use zingo_netutils::{GetClientError, GetLatestTreeStateError};
358///
359/// let e = GetLatestTreeStateError::from(GetClientError::InvalidScheme);
360/// assert!(matches!(e, GetLatestTreeStateError::GetClientError(_)));
361///
362/// let e = GetLatestTreeStateError::from(tonic::Status::internal("oops"));
363/// assert!(matches!(e, GetLatestTreeStateError::GetLatestTreeStateError(_)));
364/// ```
365#[derive(Debug, thiserror::Error)]
366pub enum GetLatestTreeStateError {
367 #[error(transparent)]
368 GetClientError(#[from] GetClientError),
369
370 #[error("gRPC error: {0}")]
371 GetLatestTreeStateError(#[from] tonic::Status),
372}
373
374/// Error from the `get_subtree_roots` (`GetSubtreeRoots`) RPC.
375///
376/// Callers can depend on:
377/// - `GetClientError` means the connection was never established.
378/// - `GetSubtreeRootsError` means the server returned a gRPC status
379/// before or instead of streaming subtree roots.
380///
381/// ```
382/// use zingo_netutils::{GetClientError, GetSubtreeRootsError};
383///
384/// let e = GetSubtreeRootsError::from(GetClientError::InvalidScheme);
385/// assert!(matches!(e, GetSubtreeRootsError::GetClientError(_)));
386///
387/// let e = GetSubtreeRootsError::from(tonic::Status::internal("oops"));
388/// assert!(matches!(e, GetSubtreeRootsError::GetSubtreeRootsError(_)));
389/// ```
390#[derive(Debug, thiserror::Error)]
391pub enum GetSubtreeRootsError {
392 #[error(transparent)]
393 GetClientError(#[from] GetClientError),
394
395 #[error("gRPC error: {0}")]
396 GetSubtreeRootsError(#[from] tonic::Status),
397}
398
399/// Error from the `ping` (`Ping`) RPC.
400///
401/// Callers can depend on:
402/// - `GetClientError` means the connection was never established.
403/// - `PingError` means the server returned a gRPC status (e.g. the
404/// server was not started with `--ping-very-insecure`).
405///
406/// ```
407/// # #[cfg(feature = "ping-very-insecure")]
408/// # {
409/// use zingo_netutils::{GetClientError, PingError};
410///
411/// let e = PingError::from(GetClientError::InvalidScheme);
412/// assert!(matches!(e, PingError::GetClientError(_)));
413///
414/// let e = PingError::from(tonic::Status::permission_denied("not enabled"));
415/// assert!(matches!(e, PingError::PingError(_)));
416/// # }
417/// ```
418#[cfg(feature = "ping-very-insecure")]
419#[derive(Debug, thiserror::Error)]
420pub enum PingError {
421 #[error(transparent)]
422 GetClientError(#[from] GetClientError),
423
424 #[error("gRPC error: {0}")]
425 PingError(#[from] tonic::Status),
426}
427
428/// Helper to construct a `tonic::Status` for testing.
429#[cfg(test)]
430fn test_status() -> tonic::Status {
431 tonic::Status::internal("test error")
432}
433
434/// Helper to construct a `GetClientError` for testing.
435#[cfg(test)]
436fn test_client_error() -> GetClientError {
437 GetClientError::InvalidScheme
438}
439
440/// Macro that generates a test module for a standard 2-variant error enum
441/// (GetClientError + tonic::Status).
442#[cfg(test)]
443macro_rules! two_variant_error_tests {
444 ($mod_name:ident, $error_type:ident, $status_variant:ident) => {
445 mod $mod_name {
446 use super::*;
447
448 #[test]
449 fn from_get_client_error() {
450 let inner = test_client_error();
451 let e = $error_type::from(inner);
452 assert!(matches!(e, $error_type::GetClientError(_)));
453 assert!(e.to_string().contains("invalid scheme"));
454 }
455
456 #[test]
457 fn from_status() {
458 let status = test_status();
459 let e = $error_type::from(status);
460 assert!(matches!(e, $error_type::$status_variant(_)));
461 assert!(e.to_string().contains("test error"));
462 }
463 }
464 };
465}
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470
471 two_variant_error_tests!(get_info, GetInfoError, GetLightdInfoError);
472 two_variant_error_tests!(get_latest_block, GetLatestBlockError, GetLatestBlockError);
473 two_variant_error_tests!(get_tree_state, GetTreeStateError, GetTreeStateError);
474 two_variant_error_tests!(get_block, GetBlockError, GetBlockError);
475 two_variant_error_tests!(
476 get_block_nullifiers,
477 GetBlockNullifiersError,
478 GetBlockNullifiersError
479 );
480 two_variant_error_tests!(get_block_range, GetBlockRangeError, GetBlockRangeError);
481 two_variant_error_tests!(
482 get_block_range_nullifiers,
483 GetBlockRangeNullifiersError,
484 GetBlockRangeNullifiersError
485 );
486 two_variant_error_tests!(get_transaction, GetTransactionError, GetTransactionError);
487 two_variant_error_tests!(get_mempool_tx, GetMempoolTxError, GetMempoolTxError);
488 two_variant_error_tests!(
489 get_mempool_stream,
490 GetMempoolStreamError,
491 GetMempoolStreamError
492 );
493 two_variant_error_tests!(
494 get_latest_tree_state,
495 GetLatestTreeStateError,
496 GetLatestTreeStateError
497 );
498 two_variant_error_tests!(
499 get_subtree_roots,
500 GetSubtreeRootsError,
501 GetSubtreeRootsError
502 );
503
504 mod send_transaction {
505 use super::*;
506
507 #[test]
508 fn from_get_client_error() {
509 let inner = test_client_error();
510 let e = SendTransactionError::from(inner);
511 assert!(matches!(e, SendTransactionError::GetClientError(_)));
512 }
513
514 #[test]
515 fn from_status() {
516 let status = test_status();
517 let e = SendTransactionError::from(status);
518 assert!(matches!(e, SendTransactionError::SendTransactionError(_)));
519 }
520
521 #[test]
522 fn send_rejected() {
523 let e = SendTransactionError::SendRejected("bad tx".into());
524 assert!(matches!(e, SendTransactionError::SendRejected(_)));
525 assert_eq!(e.to_string(), "send rejected: bad tx");
526 }
527 }
528
529 #[cfg(feature = "ping-very-insecure")]
530 two_variant_error_tests!(ping, PingError, PingError);
531}
532
533// ── TransparentIndexer errors ───────────────────────────────────────
534
535#[cfg(feature = "globally-public-transparent")]
536pub mod transparent {
537 use super::GetClientError;
538
539 /// Error from the deprecated `get_taddress_txids` (`GetTaddressTxids`) RPC.
540 ///
541 /// Callers can depend on:
542 /// - `GetClientError` means the connection was never established.
543 /// - `GetTaddressTxidsError` means the server returned a gRPC status.
544 ///
545 /// ```
546 /// # #[cfg(feature = "globally-public-transparent")]
547 /// # {
548 /// use zingo_netutils::error::transparent::GetTaddressTxidsError;
549 /// use zingo_netutils::GetClientError;
550 ///
551 /// let e = GetTaddressTxidsError::from(GetClientError::InvalidScheme);
552 /// assert!(matches!(e, GetTaddressTxidsError::GetClientError(_)));
553 ///
554 /// let e = GetTaddressTxidsError::from(tonic::Status::internal("oops"));
555 /// assert!(matches!(e, GetTaddressTxidsError::GetTaddressTxidsError(_)));
556 /// # }
557 /// ```
558 #[derive(Debug, thiserror::Error)]
559 pub enum GetTaddressTxidsError {
560 #[error(transparent)]
561 GetClientError(#[from] GetClientError),
562
563 #[error("gRPC error: {0}")]
564 GetTaddressTxidsError(#[from] tonic::Status),
565 }
566
567 /// Error from the `get_taddress_transactions` (`GetTaddressTransactions`) RPC.
568 ///
569 /// Callers can depend on:
570 /// - `GetClientError` means the connection was never established.
571 /// - `GetTaddressTransactionsError` means the server returned a gRPC status.
572 ///
573 /// ```
574 /// # #[cfg(feature = "globally-public-transparent")]
575 /// # {
576 /// use zingo_netutils::error::transparent::GetTaddressTransactionsError;
577 /// use zingo_netutils::GetClientError;
578 ///
579 /// let e = GetTaddressTransactionsError::from(GetClientError::InvalidScheme);
580 /// assert!(matches!(e, GetTaddressTransactionsError::GetClientError(_)));
581 ///
582 /// let e = GetTaddressTransactionsError::from(tonic::Status::internal("oops"));
583 /// assert!(matches!(e, GetTaddressTransactionsError::GetTaddressTransactionsError(_)));
584 /// # }
585 /// ```
586 #[derive(Debug, thiserror::Error)]
587 pub enum GetTaddressTransactionsError {
588 #[error(transparent)]
589 GetClientError(#[from] GetClientError),
590
591 #[error("gRPC error: {0}")]
592 GetTaddressTransactionsError(#[from] tonic::Status),
593 }
594
595 /// Error from the `get_taddress_balance` (`GetTaddressBalance`) RPC.
596 ///
597 /// Callers can depend on:
598 /// - `GetClientError` means the connection was never established.
599 /// - `GetTaddressBalanceError` means the server returned a gRPC status.
600 ///
601 /// ```
602 /// # #[cfg(feature = "globally-public-transparent")]
603 /// # {
604 /// use zingo_netutils::error::transparent::GetTaddressBalanceError;
605 /// use zingo_netutils::GetClientError;
606 ///
607 /// let e = GetTaddressBalanceError::from(GetClientError::InvalidScheme);
608 /// assert!(matches!(e, GetTaddressBalanceError::GetClientError(_)));
609 ///
610 /// let e = GetTaddressBalanceError::from(tonic::Status::internal("oops"));
611 /// assert!(matches!(e, GetTaddressBalanceError::GetTaddressBalanceError(_)));
612 /// # }
613 /// ```
614 #[derive(Debug, thiserror::Error)]
615 pub enum GetTaddressBalanceError {
616 #[error(transparent)]
617 GetClientError(#[from] GetClientError),
618
619 #[error("gRPC error: {0}")]
620 GetTaddressBalanceError(#[from] tonic::Status),
621 }
622
623 /// Error from the `get_taddress_balance_stream` (`GetTaddressBalanceStream`) RPC.
624 ///
625 /// Callers can depend on:
626 /// - `GetClientError` means the connection was never established.
627 /// - `GetTaddressBalanceStreamError` means the server returned a gRPC status.
628 ///
629 /// ```
630 /// # #[cfg(feature = "globally-public-transparent")]
631 /// # {
632 /// use zingo_netutils::error::transparent::GetTaddressBalanceStreamError;
633 /// use zingo_netutils::GetClientError;
634 ///
635 /// let e = GetTaddressBalanceStreamError::from(GetClientError::InvalidScheme);
636 /// assert!(matches!(e, GetTaddressBalanceStreamError::GetClientError(_)));
637 ///
638 /// let e = GetTaddressBalanceStreamError::from(tonic::Status::internal("oops"));
639 /// assert!(matches!(e, GetTaddressBalanceStreamError::GetTaddressBalanceStreamError(_)));
640 /// # }
641 /// ```
642 #[derive(Debug, thiserror::Error)]
643 pub enum GetTaddressBalanceStreamError {
644 #[error(transparent)]
645 GetClientError(#[from] GetClientError),
646
647 #[error("gRPC error: {0}")]
648 GetTaddressBalanceStreamError(#[from] tonic::Status),
649 }
650
651 /// Error from the `get_address_utxos` (`GetAddressUtxos`) RPC.
652 ///
653 /// Callers can depend on:
654 /// - `GetClientError` means the connection was never established.
655 /// - `GetAddressUtxosError` means the server returned a gRPC status.
656 ///
657 /// ```
658 /// # #[cfg(feature = "globally-public-transparent")]
659 /// # {
660 /// use zingo_netutils::error::transparent::GetAddressUtxosError;
661 /// use zingo_netutils::GetClientError;
662 ///
663 /// let e = GetAddressUtxosError::from(GetClientError::InvalidScheme);
664 /// assert!(matches!(e, GetAddressUtxosError::GetClientError(_)));
665 ///
666 /// let e = GetAddressUtxosError::from(tonic::Status::internal("oops"));
667 /// assert!(matches!(e, GetAddressUtxosError::GetAddressUtxosError(_)));
668 /// # }
669 /// ```
670 #[derive(Debug, thiserror::Error)]
671 pub enum GetAddressUtxosError {
672 #[error(transparent)]
673 GetClientError(#[from] GetClientError),
674
675 #[error("gRPC error: {0}")]
676 GetAddressUtxosError(#[from] tonic::Status),
677 }
678
679 /// Error from the `get_address_utxos_stream` (`GetAddressUtxosStream`) RPC.
680 ///
681 /// Callers can depend on:
682 /// - `GetClientError` means the connection was never established.
683 /// - `GetAddressUtxosStreamError` means the server returned a gRPC status
684 /// before or instead of streaming UTXOs.
685 ///
686 /// ```
687 /// # #[cfg(feature = "globally-public-transparent")]
688 /// # {
689 /// use zingo_netutils::error::transparent::GetAddressUtxosStreamError;
690 /// use zingo_netutils::GetClientError;
691 ///
692 /// let e = GetAddressUtxosStreamError::from(GetClientError::InvalidScheme);
693 /// assert!(matches!(e, GetAddressUtxosStreamError::GetClientError(_)));
694 ///
695 /// let e = GetAddressUtxosStreamError::from(tonic::Status::internal("oops"));
696 /// assert!(matches!(e, GetAddressUtxosStreamError::GetAddressUtxosStreamError(_)));
697 /// # }
698 /// ```
699 #[derive(Debug, thiserror::Error)]
700 pub enum GetAddressUtxosStreamError {
701 #[error(transparent)]
702 GetClientError(#[from] GetClientError),
703
704 #[error("gRPC error: {0}")]
705 GetAddressUtxosStreamError(#[from] tonic::Status),
706 }
707
708 #[cfg(test)]
709 mod tests {
710 use super::*;
711
712 fn test_status() -> tonic::Status {
713 tonic::Status::internal("test error")
714 }
715
716 fn test_client_error() -> GetClientError {
717 GetClientError::InvalidScheme
718 }
719
720 macro_rules! two_variant_error_tests {
721 ($mod_name:ident, $error_type:ident, $status_variant:ident) => {
722 mod $mod_name {
723 use super::*;
724
725 #[test]
726 fn from_get_client_error() {
727 let inner = test_client_error();
728 let e = $error_type::from(inner);
729 assert!(matches!(e, $error_type::GetClientError(_)));
730 assert!(e.to_string().contains("invalid scheme"));
731 }
732
733 #[test]
734 fn from_status() {
735 let status = test_status();
736 let e = $error_type::from(status);
737 assert!(matches!(e, $error_type::$status_variant(_)));
738 assert!(e.to_string().contains("test error"));
739 }
740 }
741 };
742 }
743
744 two_variant_error_tests!(
745 get_taddress_txids,
746 GetTaddressTxidsError,
747 GetTaddressTxidsError
748 );
749 two_variant_error_tests!(
750 get_taddress_transactions,
751 GetTaddressTransactionsError,
752 GetTaddressTransactionsError
753 );
754 two_variant_error_tests!(
755 get_taddress_balance,
756 GetTaddressBalanceError,
757 GetTaddressBalanceError
758 );
759 two_variant_error_tests!(
760 get_taddress_balance_stream,
761 GetTaddressBalanceStreamError,
762 GetTaddressBalanceStreamError
763 );
764 two_variant_error_tests!(
765 get_address_utxos,
766 GetAddressUtxosError,
767 GetAddressUtxosError
768 );
769 two_variant_error_tests!(
770 get_address_utxos_stream,
771 GetAddressUtxosStreamError,
772 GetAddressUtxosStreamError
773 );
774 }
775}